summaryrefslogtreecommitdiffstats
path: root/pacman-c++/server.cpp
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2011-05-05 00:57:07 +0200
committermanuel <manuel@mausz.at>2011-05-05 00:57:07 +0200
commitce48af53646cd9e7ec762fc1ac176b3aa620b11d (patch)
treef8fbf2cae8c7d0cbac2696a8f4cf94410bfb4928 /pacman-c++/server.cpp
parente54ccad07e256ba877bd41d70bd358bd0085bd1e (diff)
downloadfoop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.tar.gz
foop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.tar.bz2
foop-ce48af53646cd9e7ec762fc1ac176b3aa620b11d.zip
- refactorized the whole project and made a few subprojects
- replaced tcp with enet - added connect dialog - some smaller bugfixes
Diffstat (limited to 'pacman-c++/server.cpp')
-rw-r--r--pacman-c++/server.cpp900
1 files changed, 0 insertions, 900 deletions
diff --git a/pacman-c++/server.cpp b/pacman-c++/server.cpp
deleted file mode 100644
index 2311c81..0000000
--- a/pacman-c++/server.cpp
+++ /dev/null
@@ -1,900 +0,0 @@
1#include "server.h"
2#include "util.h"
3#include "pacman.pb.h"
4#include "block.h"
5#include "anyoption.h"
6#include "bonuspoint.h"
7#include "point.h"
8#include <QtNetwork/QTcpServer>
9#include <QtNetwork/QTcpSocket>
10#include <QTextStream>
11#include <sys/types.h>
12
13Server::Server(QWidget *parent)
14 : SceneHolder(parent), m_bindaddress(QHostAddress::Any),
15 m_port(Constants::Networking::port), m_numbots(0),
16 m_rounds(3), m_curRound(0), m_running(false), m_finishRound(false)
17{
18 /* determine max players by using order array */
19 for(m_maxplayers = 0; Color::order[m_maxplayers] != Color::none; ++m_maxplayers);
20}
21
22bool Server::run()
23{
24 /* create eating order list first - this can also be created dynamically per round (theoretically) */
25 for(unsigned int i = 0; i < m_maxplayers; ++i)
26 m_eatingorder.append(Color::order[i]);
27 m_eatingorder.append(m_eatingorder.at(0));
28
29 m_tickTimer = new QTimer(this);
30 m_tickTimer->setInterval(Constants::tick);
31 connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick()));
32
33 std::cout << "[Server] Running server..." << std::endl
34 << "[Server] Max players: " << m_maxplayers << std::endl
35 << "[Server] Number of bots: " << m_numbots << std::endl
36 << "[Server] Number of rounds: " << m_rounds << std::endl;
37 if (!waitForClientConnections())
38 return false;
39
40 connect(this, SIGNAL(allPointsRemoved()), this, SLOT(setFinishRound()));
41 initRoundMap();
42 return true;
43}
44
45void Server::tick()
46{
47 //qDebug() << "[Tick] Doing server update";
48 if (m_finishRound)
49 stopGame(true);
50 if (!m_running)
51 {
52 Transmission::map_t map = Util::createEmptyMap();
53 sendUpdate(map);
54 Util::deleteMap(map);
55 return;
56 }
57
58 /* let the bots move */
59 foreach (Color::Color color, m_bots)
60 botCalculate(m_actors[color]);
61
62 /* move on the virtual map */
63 Transmission::map_t map = calculateUpdates();
64 updateMap(map);
65
66 /* add a random bonus point */
67 QPoint pos = addRandomPoint(map, Transmission::bonuspoint);
68 if (!pos.isNull())
69 updateMap(map, pos.x(), pos.y());
70
71 /* add/remove random colorized block */
72 if (this->property("coloredblocks").toBool())
73 colorizeBlocks(map);
74
75 sendUpdate(map);
76 Util::deleteMap(map);
77}
78
79Transmission::map_t Server::calculateUpdates()
80{
81 QMap<Color::Color, QPair<QPoint, QPoint> > movements;
82 Transmission::map_t map = Util::createEmptyMap();
83
84 QMutableMapIterator<Color::Color, Actor::Movement> i(m_actorMovements);
85 while (i.hasNext())
86 {
87 i.next();
88 Color::Color color = i.key();
89 Actor *actor = m_actors[color];
90 QPoint mapPosition = CoordToMapPosition(actor->pos().toPoint());
91 Actor::Movement direction = i.value();
92 int turn = 0;
93
94invalid_direction:
95 ++turn;
96 qDebug() << "[Calc] Actor wants to move: color=" << color
97 << "pos=" << mapPosition << "direction=" << direction;
98
99 QPoint newMapPosition = mapPosition + Actor::movementToPoint(direction);
100 if (newMapPosition.x() < 0)
101 newMapPosition.setX(0);
102 if (newMapPosition.x() >= visualMap.size())
103 newMapPosition.setX(visualMap.size() - 1);
104 if (newMapPosition.y() < 0)
105 newMapPosition.setY(0);
106 if (newMapPosition.y() >= visualMap[newMapPosition.x()].size())
107 newMapPosition.setY(visualMap[newMapPosition.x()].size() - 1);
108
109 /* check if there's an item at new location of actor */
110 GameEntity *item = visualMap[newMapPosition.x()][newMapPosition.y()];
111 GameEntity *oldItem = visualMap[mapPosition.x()][mapPosition.y()];
112 if (item != NULL && oldItem != item)
113 {
114 qDebug() << "[Calc] Found item at new actor location";
115 if (!item->checkEnter(actor))
116 {
117 /* movement invalid. e.g. move against wall */
118 newMapPosition = mapPosition;
119 qDebug() << "[Calc] Item blocks actor";
120 }
121 else
122 {
123 /* apply actions of entering this field */
124 GameEntity::EnteredState survive = item->enter(actor);
125 if (survive == GameEntity::DestroyedEntity)
126 map[newMapPosition.x()][newMapPosition.y()] = Transmission::empty | actor->color();
127 else if (survive == GameEntity::DestroyedActor)
128 {
129 map[newMapPosition.x()][newMapPosition.y()] = Transmission::death | actor->color();
130 m_actors[item->color()]->addRoundPoints(actor->getRoundPoints());
131 actor->finishRound(true);
132 setFinishRound();
133 }
134 }
135 }
136
137 /* movement didn't work - e.g. was blocked */
138 if (mapPosition == newMapPosition)
139 {
140 /* check turn */
141 if (turn == 1 && direction != actor->direction())
142 {
143 /* set direction back to last known direction and try again */
144 qDebug() << "[Calc] Movement was blocked. Trying last known actor direction";
145 direction = actor->direction();
146 goto invalid_direction;
147 }
148 else
149 {
150 /* second turn didn't work too -> stop movement */
151 direction = Actor::None;
152 qDebug() << "[Calc] No good direction known. Movement stopped";
153 }
154 }
155
156 /* store movement for collision */
157 movements.insert(color, QPair<QPoint, QPoint>(mapPosition, newMapPosition));
158
159 /* set direction (used for collision detection) */
160 actor->setDirection(direction);
161
162 /* DEBUG: uncomments to disable auto-movement */
163 //direction = Actor::None;
164
165 /* actor is not moving anymore: remove from list */
166 if (direction == Actor::None)
167 i.remove();
168 }
169
170 /* check for actor collision */
171 QList<Actor *> blocked;
172 foreach(Color::Color color, movements.keys())
173 {
174 Actor *actor = m_actors[color];
175 QPoint oldpos = movements[color].first;
176 QPoint newpos = movements[color].second;
177 QPoint scenepos = actor->pos().toPoint();
178
179 /* first move actor to new position */
180 actor->move(actor->direction());
181
182 /* next check for collisions */
183 Actor *orderer = NULL;
184 Actor *meal = NULL;
185 foreach(Actor *other, m_actors)
186 {
187 if (actor == other)
188 continue;
189 if (!actor->collidesWithItem(other))
190 continue;
191 /* both move in the same direction */
192 if (actor->direction() == other->direction())
193 continue;
194
195 if (other->canEat(actor, m_eatingorder))
196 {
197 qDebug() << "[Collision] Actor" << actor->color() << "got EATEN by" << other->color();
198 orderer = other;
199 meal = actor;
200 break;
201 }
202 else if (actor->canEat(other, m_eatingorder))
203 {
204 qDebug() << "[Collision] Actor" << actor->color() << "EATS" << other->color();
205 orderer = actor;
206 meal = other;
207 blocked.append(other);
208 break;
209 }
210 else
211 {
212 qDebug() << "[Collision] Actor" << actor->color() << "got BLOCKED by" << other->color();
213 blocked.append(actor);
214 /* no break here */
215 }
216 }
217
218 /* update map depending on collision */
219 if (orderer != NULL && meal != NULL)
220 {
221 map[newpos.x()][newpos.y()] |= Transmission::pacman | Transmission::death
222 | orderer->color() | meal->color();
223 orderer->addRoundPoints(meal->getRoundPoints());
224 meal->finishRound(true);
225 setFinishRound();
226 }
227 else if (blocked.contains(actor))
228 {
229 /* move actor back early cause he won't move */
230 actor->setPos(scenepos);
231 map[oldpos.x()][oldpos.y()] |= Transmission::pacman | color;
232 }
233 else
234 {
235 map[newpos.x()][newpos.y()] |= Transmission::pacman | color;
236 }
237 }
238
239 /* move all actors back to their origin position */
240 foreach(Color::Color color, movements.keys())
241 m_actors[color]->setPos(mapPositionToCoord(movements[color].first));
242
243#ifndef QT_NO_DEBUG
244 /* collision sanity check: colors must be unique */
245 foreach(Color::Color col, m_eatingorder.toSet())
246 {
247 QList<QPoint> found;
248 for (unsigned int x = 0; x < Constants::map_size.width; ++x)
249 {
250 for (unsigned int y = 0; y < Constants::map_size.height; ++y)
251 {
252 if ((map[x][y] & Transmission::color_mask) & col)
253 found.append(QPoint(x, y));
254 }
255 }
256 if (found.count() > 1)
257 qCritical() << "[Collision] found" << found.count() << "fields with color=" << col;
258 }
259#endif
260
261 return map;
262}
263
264QPoint Server::addRandomPoint(Transmission::map_t map, Transmission::field_t type)
265{
266 int chance = Constants::Game::bouns_point_chance
267 - Constants::Game::bouns_point_chance_playerfactor * (m_maxplayers - 1);
268 int rand = (int) (chance * (qrand() / (RAND_MAX + 1.0)));
269 if (rand != 0)
270 return QPoint();
271
272 /* get list of valid positions */
273 QList<QPoint> validpos;
274 for (unsigned int x = 0; x < Constants::map_size.width; ++x)
275 {
276 for (unsigned int y = 0; y < Constants::map_size.height; ++y)
277 {
278 if (visualMap[x][y] == NULL)
279 validpos.append(QPoint(x, y));
280 }
281 }
282
283 /* remove actors positions from list
284 * performance would be better if actors would be listed in visualMap too
285 * but this isn't possible that easily. see comment in updateMap(map)
286 */
287 foreach (Actor *actor, m_actors)
288 {
289 QPoint actorpos = CoordToMapPosition(actor->pos().toPoint());
290 validpos.removeAll(actorpos);
291 }
292
293 if (validpos.empty())
294 return QPoint();
295
296 rand = (int) (validpos.size() * (qrand() / (RAND_MAX + 1.0)));
297 QPoint pos = validpos.at(rand);
298 map[pos.x()][pos.y()] = type;
299 return pos;
300}
301
302void Server::colorizeBlocks(Transmission::map_t map)
303{
304 /* decrement tickcount of colored blocks */
305 if (!m_coloredBlocks.empty())
306 {
307 QMutableMapIterator<QPoint, unsigned int> i(m_coloredBlocks);
308 while(i.hasNext())
309 {
310 i.next();
311 unsigned val = i.value();
312 if (val > 0)
313 i.setValue(--val);
314 else
315 {
316 QPoint block = i.key();
317 /* check for actor collision */
318 bool skip = false;
319 foreach (Actor *actor, m_actors)
320 {
321 if (CoordToMapPosition(actor->pos().toPoint()) == block)
322 skip = true;
323 if (skip)
324 break;
325 }
326 if (skip)
327 continue;
328
329 map[block.x()][block.y()] |= Transmission::block | Color::none;
330 updateMap(map, block.x(), block.y());
331 m_blocks.append(block);
332 i.remove();
333 }
334 }
335 }
336
337 /* colorize a random block */
338 int rand = (int) (Constants::Game::colorize_block_chance * (qrand() / (RAND_MAX + 1.0)));
339 if (rand == 0)
340 {
341 rand = (int) (m_blocks.size() * (qrand() / (RAND_MAX + 1.0)));
342 QPoint block = m_blocks.at(rand);
343 m_blocks.removeAt(rand);
344
345 unsigned int min = Constants::Game::colorize_block_tickcount_min;
346 unsigned int max = Constants::Game::colorize_block_tickcount_max;
347 int tickcount = min + (int) ((max - min + 1) * (qrand() / (RAND_MAX + 1.0)));
348 m_coloredBlocks.insert(block, tickcount);
349
350 unsigned int color = (int) (m_maxplayers * (qrand() / (RAND_MAX + 1.0)));
351 map[block.x()][block.y()] |= Transmission::block | Color::order[color];
352 updateMap(map, block.x(), block.y());
353 }
354}
355
356bool Server::waitForClientConnections()
357{
358 // server must stay alive as long as sockets (qt parent mem mechanism)
359 QTcpServer *tcpSrv = new QTcpServer(this);
360 tcpSrv->listen(m_bindaddress, m_port);
361 if (!tcpSrv->isListening())
362 {
363 qCritical() << "Error while creating socket:" << qPrintable(tcpSrv->errorString());
364 return false;
365 }
366 std::cout << "[Server] Listening on: "
367 << qPrintable(QString("%1:%2").arg(tcpSrv->serverAddress().toString())
368 .arg(tcpSrv->serverPort())) << std::endl;
369
370 std::cout << "[Server] Waiting for clients" << std::endl;
371 ProtoBuf::Init packet;
372 packet.set_maxplayers(m_maxplayers);
373 for (unsigned int i = 0; i < (m_maxplayers - m_numbots); ++i)
374 {
375 bool connectionAvailable = tcpSrv->waitForNewConnection(-1);
376 Q_ASSERT(connectionAvailable);
377 Q_UNUSED(connectionAvailable);
378 QTcpSocket *socket = tcpSrv->nextPendingConnection();
379 connect(socket, SIGNAL(readyRead()), this, SLOT(keyPressUpdate()));
380
381 /* assign color and notify client */
382 Color::Color color = Color::order[i];
383 m_clientConnections[color] = socket;
384 packet.set_color(color);
385 Util::sendPacket(packet, socket);
386 std::cout << "[Connect] New Player: color=" << qPrintable(Util::colorToString(color)) << std::endl;
387 }
388
389 for (unsigned int i = (m_maxplayers - m_numbots); i < m_maxplayers; ++i)
390 {
391 m_bots.append(Color::order[i]);
392 m_actorMovements[Color::order[i]] = Actor::None;
393 }
394
395 qDebug() << "[Server] All Clients connected";
396 return true;
397}
398
399void Server::sendUpdate(Transmission::map_t map, bool firstPacket)
400{
401 m_updatepacket.Clear();
402
403 for (unsigned int x = 0; x < Constants::map_size.width; ++x)
404 {
405 for (unsigned int y = 0; y < Constants::map_size.height; ++y)
406 m_updatepacket.add_field(map[x][y]);
407 }
408
409 for(unsigned int i = 0; i < m_maxplayers; ++i)
410 {
411 m_updatepacket.add_round_points(m_actors.value(Color::order[i])->getRoundPoints());
412 m_updatepacket.add_game_points(m_actors.value(Color::order[i])->getGamePoints());
413 }
414
415 /* we send the eating_order inside the first packet */
416 if (firstPacket)
417 {
418 foreach(Color::Color color, m_eatingorder)
419 m_updatepacket.add_eating_order(color);
420 }
421
422 QSharedPointer<QByteArray> data = Util::createPacket(m_updatepacket);
423 QMutableMapIterator<Color::Color, QTcpSocket *> i(m_clientConnections);
424 while(i.hasNext())
425 {
426 i.next();
427 QTcpSocket *socket = i.value();
428
429 if (!Util::sendPacket(data.data(), socket))
430 {
431 qWarning() << "[Connect] Error while sending data to client" << i.key() << "-> Disconnecting...";
432 socket->close();
433 std::cout << "[Connect] Actor color=" << qPrintable(Util::colorToString(i.key()))
434 << " is now a bot" << std::endl;
435 m_bots.append(i.key());
436 ++m_numbots;
437 i.remove();
438 }
439 }
440
441 if (m_maxplayers == m_numbots)
442 {
443 std::cout << "[Connect] No more real players left. Exiting..." << std::endl;
444 qApp->quit();
445 }
446}
447
448void Server::botCalculate(Actor *actor)
449{
450 QPoint actorpos = CoordToMapPosition(actor->pos().toPoint());
451
452 /* first make list of possible directions based on current actor position */
453 QHash<Actor::Movement, unsigned int> directions;
454 if (actorpos.x() > 0)
455 directions.insert(Actor::Left, 0);
456 if (actorpos.x() < visualMap.size() - 1)
457 directions.insert(Actor::Right, 0);
458 if (actorpos.y() > 0)
459 directions.insert(Actor::Up, 0);
460 if (actorpos.y() < visualMap[actorpos.x()].size() - 1)
461 directions.insert(Actor::Down, 0);
462
463 /* check neighbours for blocks first */
464 QMutableHashIterator<Actor::Movement, unsigned int> i(directions);
465 while(i.hasNext())
466 {
467 i.next();
468 QPoint newpos = actorpos + Actor::movementToPoint(i.key());
469 if (newpos.x() < 0 || newpos.x() >= visualMap.size())
470 continue;
471 if (newpos.y() < 0 || newpos.y() >= visualMap[newpos.x()].size())
472 continue;
473 GameEntity *item = visualMap[newpos.x()][newpos.y()];
474
475 /* check if neighbour is a block */
476 Block *block = qgraphicsitem_cast<Block *>(item);
477 if (block != NULL && block->color() != actor->color())
478 i.remove();
479 }
480
481 /* we're enclosed by blocks */
482 if (directions.empty())
483 return;
484
485 /* determine if other actors are in range to be afraid/to hunt */
486 int mindistance = Constants::AI::player_minimum_distance;
487 QList<QPoint> pos_afraid;
488 QList<QPoint> pos_hunt;
489 foreach (Actor *other, m_actors)
490 {
491 if (actor == other)
492 continue;
493
494 QList<QPoint> *ptr = NULL;
495 if (other->canEat(actor, m_eatingorder))
496 ptr = &pos_afraid;
497 else if (actor->canEat(other, m_eatingorder))
498 ptr = &pos_hunt;
499 if (ptr == NULL)
500 continue;
501
502 QPoint otherpos = CoordToMapPosition(other->pos().toPoint());
503 QPoint distance = actorpos - otherpos;
504 if (distance.manhattanLength() < mindistance)
505 ptr->append(otherpos);
506 }
507
508 /* check new directions and change direction-weight */
509 i = directions;
510 while(i.hasNext())
511 {
512 i.next();
513 QPoint newpos = actorpos + Actor::movementToPoint(i.key());
514
515 /* check for new positions in afraid list */
516 foreach(QPoint otherpos, pos_afraid)
517 {
518 int olddistance = (actorpos - otherpos).manhattanLength();
519 int newdistance = (newpos - otherpos).manhattanLength();
520 if (newdistance >= olddistance)
521 i.setValue(i.value() + Constants::AI::weight_afraid);
522
523 /* check for blocks of own color: other pacman can't follow their */
524 GameEntity *item = visualMap[newpos.x()][newpos.y()];
525 Block *block = qgraphicsitem_cast<Block *>(item);
526 if (block != NULL && block->color() == actor->color())
527 i.setValue(i.value() + Constants::AI::weight_colorblock);
528 }
529
530 /* check for new positions in hunt list */
531 foreach(QPoint otherpos, pos_hunt)
532 {
533 int olddistance = (actorpos - otherpos).manhattanLength();
534 int newdistance = (newpos - otherpos).manhattanLength();
535 if (newdistance <= olddistance)
536 i.setValue(i.value() + Constants::AI::weight_hunt);
537 }
538
539 /* check for bonuspoint */
540 GameEntity *item = visualMap[newpos.x()][newpos.y()];
541 BonusPoint *bpoint = qgraphicsitem_cast<BonusPoint *>(item);
542 if (bpoint != NULL)
543 i.setValue(i.value() + Constants::AI::weight_bonus_point);
544
545 /* check for normal point */
546 Point *point = qgraphicsitem_cast<Point *>(item);
547 if (point != NULL)
548 i.setValue(i.value() + Constants::AI::weight_point);
549 }
550
551 /* sort directions */
552 QList<unsigned int> weightlist = directions.values();
553 qSort(weightlist.begin(), weightlist.end(), qGreater<unsigned int>());
554
555 /* remove directions with lesser weight */
556 unsigned int max = weightlist.at(0);
557 i = directions;
558 while(i.hasNext())
559 {
560 i.next();
561 if (i.value() < max)
562 i.remove();
563 }
564
565 QList<Actor::Movement> list = directions.keys();
566
567 /* default is no direction change */
568 if (list.contains(actor->direction()))
569 return;
570
571 /* random direction */
572 int rand = (int) (list.size() * (qrand() / (RAND_MAX + 1.0)));
573 m_actorMovements[actor->color()] = list.at(rand);
574}
575
576void Server::keyPressUpdate()
577{
578 ProtoBuf::KeyPressUpdate packet;
579 QMapIterator<Color::Color, QTcpSocket *> i(m_clientConnections);
580 while (i.hasNext())
581 {
582 i.next();
583 Color::Color color = i.key();
584 QTcpSocket *socket = i.value();
585 QDataStream in(i.value());
586 while (socket->bytesAvailable() > (qint64)sizeof(qint64))
587 {
588 QSharedPointer<QByteArray> data = Util::receivePacket(socket);
589 bool worked = packet.ParseFromArray(data->data(), data->size());
590 Q_ASSERT(worked);
591 Q_UNUSED(worked);
592 Transmission::field_t direction = packet.newkey();
593 qDebug() << "[KeyPress] actor=" << color << "direction=" << direction;
594 m_actorMovements[color] = Util::transmissionMovementToActor(direction);
595 }
596 }
597}
598
599void Server::initRoundMap()
600{
601 std::cout << "[Game] New round starts..." << std::endl;
602 m_tickTimer->stop();
603
604 /* reset scene and clean up items */
605 reset();
606
607 /* randomize color eating order */
608 m_eatingorder.removeLast();
609 random_shuffle(m_eatingorder.begin(), m_eatingorder.end());
610 m_eatingorder.append(m_eatingorder.at(0));
611
612 /* create new map */
613 Transmission::map_t map = Util::createDemoMap();
614 Util::placeActors(map, m_maxplayers, Color::order);
615 Util::fillPoints(map);
616
617#if 0 // actor eating actor tests - TODO: remove
618 m_actorMovements.clear();
619#if 0
620 //works
621 map[0][0] = Color::order[0] | Transmission::pacman;
622 map[0][1] = Color::order[1] | Transmission::pacman;
623 m_actorMovements.insert(Color::order[0], Actor::Down);
624#elif 0
625 //works
626 map[0][0] = Color::order[0] | Transmission::pacman;
627 map[0][3] = Color::order[1] | Transmission::pacman;
628 m_actorMovements.insert(Color::order[0], Actor::Down);
629#elif 0
630 //works
631 map[0][0] = Color::order[0] | Transmission::pacman;
632 map[0][4] = Color::order[1] | Transmission::pacman;
633 m_actorMovements.insert(Color::order[0], Actor::Down);
634#elif 0
635 //works
636 map[0][0] = Color::order[0] | Transmission::pacman;
637 map[0][5] = Color::order[1] | Transmission::pacman;
638 m_actorMovements.insert(Color::order[0], Actor::Down);
639#elif 0
640 //works
641 map[0][0] = Color::order[1] | Transmission::pacman;
642 map[0][5] = Color::order[0] | Transmission::pacman;
643 m_actorMovements.insert(Color::order[1], Actor::Down);
644#elif 0
645 //works
646 map[0][0] = Color::order[0] | Transmission::pacman;
647 map[0][5] = Color::order[1] | Transmission::pacman;
648 m_actorMovements.insert(Color::order[0], Actor::Down);
649 m_actorMovements.insert(Color::order[1], Actor::Up);
650#elif 0
651 //works
652 map[0][0] = Color::order[0] | Transmission::pacman;
653 map[0][6] = Color::order[1] | Transmission::pacman;
654 m_actorMovements.insert(Color::order[0], Actor::Down);
655 m_actorMovements.insert(Color::order[1], Actor::Up);
656#elif 0
657 //works
658 map[0][0] = Color::order[0] | Transmission::pacman;
659 map[0][7] = Color::order[1] | Transmission::pacman;
660 m_actorMovements.insert(Color::order[0], Actor::Down);
661 m_actorMovements.insert(Color::order[1], Actor::Up);
662#elif 1
663 //works
664 map[0][0] = Color::order[0] | Transmission::pacman;
665 map[0][1] = Color::order[1] | Transmission::pacman;
666 m_actorMovements.insert(Color::order[0], Actor::Down);
667 m_actorMovements.insert(Color::order[1], Actor::Down);
668#endif
669#endif
670
671 /* save positions of blocks for later usage */
672 m_blocks.clear();
673 for (unsigned int x = 0; x < Constants::map_size.width; ++x)
674 {
675 for (unsigned int y = 0; y < Constants::map_size.height; ++y)
676 {
677 Transmission::field_t &cur = map[x][y];
678 if (cur & Transmission::block)
679 m_blocks.append(QPoint(x, y));
680 }
681 }
682
683 updateMap(map);
684 sendUpdate(map, true);
685 Util::deleteMap(map);
686 map = NULL;
687
688 m_actorMovements.clear();
689
690 disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL);
691 connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(startGame()));
692 AudioManager::self()->play(Sound::Intro, true);
693 m_tickTimer->start();
694}
695
696void Server::startGame()
697{
698 m_running = true;
699}
700
701void Server::stopGame(bool delay)
702{
703 /* first finish previous round */
704 foreach(Actor *actor, m_actors)
705 actor->finishRound();
706 m_finishRound = false;
707 m_running = false;
708
709 /* add delay if requested */
710 if (delay)
711 {
712 disconnect(AudioManager::self()->audioPlayer(), NULL, this, NULL);
713 connect(AudioManager::self()->audioPlayer(), SIGNAL(finished()), this, SLOT(stopGame()));
714 AudioManager::self()->play(Sound::Die, true);
715 return;
716 }
717
718 std::cout << "[Game] Round finished..." << std::endl;
719
720 /* do next-round work */
721 ++m_curRound;
722 if(m_rounds == 0 || m_curRound < m_rounds)
723 initRoundMap();
724 else
725 {
726 /* end of game */
727 std::cout << "[Game] All round finished. Exiting..." << std::endl;
728 qApp->quit();
729 }
730}
731
732void Server::setFinishRound()
733{
734 m_finishRound = true;
735}
736
737bool Server::parseCommandline()
738{
739 AnyOption opt;
740 opt.setVerbose();
741
742 /* usage strings must remain valid until parsing is done */
743 QString exec = QFileInfo(qApp->applicationFilePath()).fileName();
744 QByteArray usage;
745 QTextStream out(&usage, QIODevice::ReadWrite | QIODevice::Text);
746 out << "Usage: " << exec << " [OPTION]" << endl
747 << "Usage: " << exec << " -h" << endl
748 << endl;
749 out << " -b, --bind <bind_address>" << endl
750 << " Specifies the ip address on which the server listens for connections" << endl
751 << " Default: " << m_bindaddress.toString() << endl
752 << endl;
753 opt.setOption("bind", 'b');
754 out << " -p, --port <port>" << endl
755 << " Specifies the port on which the server listens for connections" << endl
756 << " Default: " << m_port << endl
757 << endl;
758 opt.setOption("port", 'p');
759 out << " -m, --maxplayers [1.." << m_maxplayers << "]" << endl
760 << " Specifies the maximum number of players/pacmans" << endl
761 << " Default: " << m_maxplayers << endl
762 << endl;
763 opt.setOption("maxplayers", 'm');
764 out << " --bots [0..maxplayers-1]" << endl
765 << " Specifies the number of AI pacmans/bots" << endl
766 << " Default: " << m_numbots << endl
767 << endl;
768 opt.setOption("bots");
769 out << " --nocolorblocks" << endl
770 << " Disable random colorized blocks" << endl
771 << endl;
772 opt.setOption("rounds", 'r');
773 out << " -r, --rounds [0 | 1..n]" << endl
774 << " Number of rounds to play" << endl
775 << " Default: " << m_rounds << endl
776 << endl;
777 opt.setFlag("nocolorblocks");
778 out << " -h, --help" << endl
779 << " Prints this help message" << endl;
780 opt.setFlag("help", 'h');
781 out.flush();
782 opt.addUsage(usage.constData());
783 opt.processCommandArgs(qApp->argc(), qApp->argv());
784
785 if (opt.getFlag("help") || opt.getFlag('h'))
786 {
787 opt.printUsage();
788 return false;
789 }
790
791 if (opt.getValue("port") != NULL)
792 {
793 bool ok;
794 m_port = QString(opt.getValue("port")).toUInt(&ok);
795 if (!ok || m_port < 1 || m_port > 65535)
796 {
797 qCritical() << "Invalid port-option:" << opt.getValue("port") << endl
798 << "Port must be between 1 and 65535";
799 return false;
800 }
801 }
802
803 if (opt.getValue("bind") != NULL)
804 {
805 m_bindaddress = opt.getValue("bind");
806 if (m_bindaddress.isNull())
807 {
808 qCritical() << "Invalid bind-option:" << opt.getValue("bind") << endl
809 << "Bind address must be an ip address";
810 return false;
811 }
812 }
813
814 if (opt.getValue("maxplayers") != NULL)
815 {
816 bool ok;
817 unsigned int maxplayers = QString(opt.getValue("maxplayers")).toUInt(&ok);
818 if (!ok || maxplayers < 1 || maxplayers > m_maxplayers)
819 {
820 qCritical() << "Invalid maxplayers-option:" << opt.getValue("maxplayers") << endl
821 << "Maxplayers must be between 1 and" << m_maxplayers;
822 return false;
823 }
824 m_maxplayers = maxplayers;
825 if (m_maxplayers == 2)
826 {
827 qCritical() << "2 player game is not supported (who wins if a player gets eaten?)";
828 return false;
829 }
830 }
831
832 if (opt.getValue("bots") != NULL)
833 {
834 bool ok;
835 unsigned int numbots = QString(opt.getValue("bots")).toUInt(&ok);
836 if (!ok || numbots >= m_maxplayers)
837 {
838 qCritical() << "Invalid numbots-options:" << opt.getValue("bots") << endl
839 << "AI pacmans/bots must be between 0 and" << m_maxplayers - 1;
840 return false;
841 }
842 m_numbots = numbots;
843 }
844
845 if (opt.getValue("rounds") != NULL)
846 {
847 bool ok;
848 unsigned int rounds = QString(opt.getValue("rounds")).toUInt(&ok);
849 if (!ok)
850 {
851 qCritical() << "Invalid number of rounds: " << opt.getValue("rounds") << endl;
852 return false;
853 }
854 m_rounds = rounds;
855 }
856
857 this->setProperty("coloredblocks", !opt.getFlag("nocolorblocks"));
858
859 return true;
860}
861
862bool operator<(const QPoint& lhs, const QPoint& rhs)
863{
864 if (lhs.x() < rhs.x())
865 return true;
866 else if (lhs.x() == rhs.x())
867 return lhs.y() < rhs.y();
868 else
869 return false;
870}
871
872bool Constants::server = true;
873
874int main(int argc, char **argv)
875{
876 /* Verify that the version of the library that we linked against is
877 * compatible with the version of the headers we compiled against.
878 */
879 GOOGLE_PROTOBUF_VERIFY_VERSION;
880
881 QApplication app(argc, argv, false);
882 app.setApplicationName("Pacman Server");
883 app.setWindowIcon(QIcon(":/appicon"));
884
885 qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
886
887 int ret = 0;
888 Server server;
889 if (!ret && !server.parseCommandline())
890 ret = 1;
891 if (!ret && !server.run())
892 ret = 1;
893 if (!ret)
894 ret = app.exec();
895
896 /* Delete all global objects allocated by libprotobuf */
897 google::protobuf::ShutdownProtobufLibrary();
898
899 return ret;
900}