1
// Copyright (c) 2001-2004 Rob Kaper <cap@capsi.com>,
2
// 2001 Erik Bourget <ebourg@cs.mcgill.ca>
4
// This program is free software; you can redistribute it and/or
5
// modify it under the terms of the GNU General Public License
6
// version 2 as published by the Free Software Foundation.
8
// This program is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
// General Public License for more details.
13
// You should have received a copy of the GNU General Public License
14
// along with this program; see the file COPYING. If not, write to
15
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16
// Boston, MA 02111-1307, USA.
23
#include <sys/socket.h>
24
#include <sys/types.h>
25
#include <netinet/in.h>
31
#include <libcapsinetwork/socket.h>
37
#include "gameobject.h"
38
#include "gameconfig.h"
43
MonopdServer::MonopdServer() : GameObject(0)
45
m_nextGameId = m_nextPlayerId = 1;
48
m_gatorHost = "monopd.unixcode.org";
50
m_gatorFrequency = 60;
51
m_useMonopigator = false;
52
m_monopigatorEvent = 0;
58
void MonopdServer::setPort(int port)
63
MonopdServer::~MonopdServer()
65
// We are responsible for the objects allocated
66
while (!m_events.empty()) { delete *m_events.begin(); m_events.erase(m_events.begin()); }
67
while (!m_games.empty()) { delete *m_games.begin(); m_games.erase(m_games.begin()); }
68
while (!m_gameConfigs.empty()) { delete *m_gameConfigs.begin(); m_gameConfigs.erase(m_gameConfigs.begin()); }
69
while (!m_players.empty()) { delete *m_players.begin(); m_players.erase(m_players.begin()); }
72
Event *MonopdServer::newEvent(unsigned int eventType, Game *game, int id)
74
Event *newEvent = new Event(id, (Event::EventType)eventType, game);
75
m_events.push_back(newEvent);
76
printf("newEvent %d/%d\n", id, m_events.size());
80
void MonopdServer::delEvent(Event *event)
82
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
85
printf("delEvent %d/%d\n", event->id(), m_events.size()-1);
92
Event *MonopdServer::findEvent(Game *game, unsigned int eventType)
95
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (event = *it) ; ++it)
96
if (event->game() == game && event->type() == eventType)
102
Event *MonopdServer::findEvent(Game *game, GameObject *object)
104
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
105
if ( (*it)->game() == game && (*it)->object() == object)
111
void MonopdServer::newGame(Player *player, const std::string gameType)
113
Game *game = new Game(m_nextGameId++);
114
m_games.push_back(game);
116
game->setProperty("name", (gameType.size() ? gameType : "atlantic"), this);
118
// Hardcoded reasonable defaults, which also ensure the entire server is the scope
119
game->setProperty("master", -1, this); // addPlayer will set the correct playerid
120
game->setProperty("description", "", this);
121
game->setProperty("minplayers", 1, this);
122
game->setProperty("maxplayers", 1, this);
123
game->setProperty("players", 0, this);
124
game->setBoolProperty("canbejoined", true, this);
126
game->loadGameType(gameType);
128
if (!game->isValid())
130
delGame(game, false);
131
player->ioWrite("<monopd><msg type=\"error\" value=\"Could not load valid configuration for gametype %s.\"/></monopd>\n", gameType.c_str());
136
syslog( LOG_INFO, "new game: id=[%d], type=[%s], games=[%d]", game->id(), gameType.c_str(), m_games.size() );
138
game->addPlayer(player, true);
140
// FIXME: DEPRECATED 1.0
141
ioWrite(std::string("<monopd><updategamelist type=\"add\"><game id=\"") + itoa(game->id()) + "\" players=\"1\" gametype=\"" + game->gameType() + "\" name=\"" + game->name() + "\" description=\"" + game->getStringProperty("description") + "\" canbejoined=\"" + itoa(game->getBoolProperty("canbejoined")) + "\"/></updategamelist></monopd>\n");
144
void MonopdServer::joinGame(Player *pInput, unsigned int gameId, const bool &spectator)
146
Game *game = findGame(gameId);
149
pInput->ioError("There is no game with id %ld.", gameId);
155
GameObject *config = game->findConfigOption( "allowspectators" );
156
if ( config && config->getBoolProperty( "value" ) && game->status() == Game::Run )
158
game->addPlayer( pInput, false, true );
159
game->ioInfo("%s joins as spectator.", pInput->name().c_str());
162
pInput->ioError("Game %ld doesn't allow spectators.", gameId);
166
int maxPlayers = game->getIntProperty("maxplayers");
167
if (game->players() >= maxPlayers)
169
pInput->ioError("This game already has the maximum of %d players.", maxPlayers);
173
if (game->status() != Game::Config)
175
pInput->ioError("You cannot join game %d, it is already in progress.", game->id());
179
game->addPlayer(pInput);
181
// FIXME: DEPRECATED 1.0
182
ioWrite(std::string("<monopd><updategamelist type=\"edit\"><game id=\"") + itoa(game->id()) + "\" players=\"" + itoa(game->players()) + "\" canbejoined=\"" + itoa(game->getBoolProperty("canbejoined")) + "\"/></updategamelist></monopd>\n");
185
void MonopdServer::exitGame(Game *game, Player *pInput)
187
game->removePlayer(pInput);
188
if (game->connectedPlayers() == 0)
193
// Send new game list
194
sendGameList(pInput);
197
Game *MonopdServer::findGame(unsigned int gameId)
200
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (game = *it) ; ++it)
201
if (game->id() == gameId)
207
void MonopdServer::delGame(Game *game, bool verbose)
210
ioWrite("<monopd><deletegame gameid=\"" + itoa(game->id()) + "\"/></monopd>\n");
212
// Remove everyone from the game
213
while (game->players())
216
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
217
if (player->game() == game)
219
game->removePlayer(player);
225
// Remove events from game
226
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
228
if ( (*it)->game() == game )
231
it = m_events.begin();
237
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (*it) ; ++it)
240
// FIXME: DEPRECATED 1.0
242
ioWrite(std::string("<monopd><updategamelist type=\"del\"><game id=\"") + itoa(game->id()) + "\"/></updategamelist></monopd>\n");
243
syslog( LOG_INFO, "del game: id=[%d], games=[%d]", game->id(), m_games.size() - 1 );
250
void MonopdServer::setGameDescription(Player *pInput, const std::string data)
252
Game *game = pInput->game();
253
if (pInput == game->master())
255
game->setProperty("description", data);
257
// FIXME: DEPRECATED 1.0
258
ioWrite("<monopd><updategamelist type=\"edit\"><game id=\"" + itoa(game->id()) + "\" gametype=\"" + game->gameType() + "\" name=\"" + game->name() + "\" description=\"" + game->getStringProperty("description") + "\"/></updategamelist></monopd>\n");
261
pInput->ioError("Only the master can set the game description!");
264
Player *MonopdServer::newPlayer(Socket *socket, const std::string &name)
266
// Players completed the handshake, delete socket timeout event
267
delSocketTimeoutEvent( socket->fd() );
269
Player *player = new Player(socket, m_nextPlayerId++);
270
m_players.push_back(player);
273
player->setProperty("game", -1, this);
274
player->setProperty("host", socket->fqdn(), this);
275
setPlayerName(player, name);
277
player->sendClientMsg();
278
sendGameList(player, true);
280
syslog( LOG_INFO, "new player: id=[%d], fd=[%d], name=[%s], players=[%d]", player->id(), socket->fd(), name.c_str(), m_players.size() );
282
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
283
printf(" player %16s %16s game %d bankrupt %d socket %d fd %d\n", player->name().c_str(), player->getStringProperty("host").c_str(), (player->game() ? player->game()->id() : -1), player->getBoolProperty("bankrupt"), player->socket(), player->socket() ? (int)player->socket()->fd() : -1);
285
// Re-register to meta server with updated player count.
286
registerMonopigator();
287
if (m_monopigatorEvent)
288
m_monopigatorEvent->setLaunchTime(time(0) + m_monopigatorEvent->frequency() );
293
void MonopdServer::reconnectPlayer(Player *pInput, const std::string &cookie)
295
Player *player = findCookie(cookie);
298
pInput->ioError("Invalid cookie.");
302
Game *game = player->game();
305
if (player->socket())
306
pInput->ioError("Cannot reconnect, target player already has a socket.");
309
pInput->ioInfo("Reconnecting.");
310
player->setSocket(pInput->socket());
311
player->sendClientMsg();
312
sendGameList(player, true);
313
if (game->status() == Game::Run)
315
game->setStatus(Game::Init);
316
game->sendFullUpdate(player);
317
game->setStatus(Game::Run);
318
game->sendStatus(player);
321
game->sendFullUpdate(player);
323
game->ioInfo("%s reconnected.", player->name().c_str());
324
pInput->setSocket(0);
330
void MonopdServer::delPlayer(Player *player)
332
// Delete timeout event, if present.
333
Game *game = player ? player->game() : 0;
334
if ( player && game )
336
Event *event = findEvent( game, dynamic_cast<GameObject *> (player) );
339
printf("cleared event for player\n");
340
event->setObject( 0 );
342
game->removePlayer( player );
345
ioWrite("<monopd><deleteplayer playerid=\"" + itoa(player->id()) + "\"/></monopd>\n");
347
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
350
removeFromScope(player);
351
syslog( LOG_INFO, "del player: id=[%d], fd=[%d], name=[%s], players=[%d]", player->id(), player->socket() ? player->socket()->fd() : 0, player->getStringProperty("name").c_str(), m_players.size() - 1 );
352
printf("delPlayer %d/%d\n", player->id(), m_players.size()-1);
359
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
360
printf(" player %16s %16s game %d bankrupt %d socket %d fd %d\n", player->name().c_str(), player->getStringProperty("host").c_str(), (player->game() ? player->game()->id() : -1), player->getBoolProperty("bankrupt"), player->socket(), player->socket() ? (int)player->socket()->fd() : -1);
362
// Re-register to meta server with updated player count.
363
registerMonopigator();
364
if (m_monopigatorEvent)
365
m_monopigatorEvent->setLaunchTime(time(0) + m_monopigatorEvent->frequency() );
368
Player *MonopdServer::findPlayer(int playerId)
371
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
372
if (player->id() == playerId)
378
Player *MonopdServer::findPlayer(Socket *socket)
381
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
382
if (player->socket() == socket)
388
Player *MonopdServer::findPlayer(const std::string &name)
391
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
392
if (player->getStringProperty("name") == name)
398
Player *MonopdServer::findCookie(const std::string &cookie)
401
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
402
if (player->getStringProperty("cookie") == cookie)
408
GameConfig *MonopdServer::newGameConfig(const std::string id, const std::string name, const std::string description)
410
GameConfig *newGameConfig = new GameConfig(id, name, description);
411
m_gameConfigs.push_back(newGameConfig);
412
syslog( LOG_INFO, "loaded game configuration: game=[%s]", name.c_str() );
413
return newGameConfig;
416
void MonopdServer::delGameConfig(GameConfig *gameConfig)
418
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin(); it != m_gameConfigs.end() && (*it) ; ++it)
419
if (*it == gameConfig)
422
m_gameConfigs.erase(it);
427
void MonopdServer::closedSocket(Socket *socket)
429
Player *pInput = findPlayer(socket);
432
// Delete socket timeout event, socket is closed already
433
delSocketTimeoutEvent( socket->fd() );
437
Game *game = pInput->game();
444
pInput->setSocket( 0 );
445
printf("%s socket 0 spec %d, bank %d, gamerun %d\n", pInput->name().c_str(), pInput->getBoolProperty("spectator"), pInput->getBoolProperty("bankrupt"), game->status() == Game::Run );
446
game->ioInfo("Connection with %s lost.", pInput->name().c_str());
448
// Only remove from game when game not running, or when it's merely a spectator.
449
bool exitFromGame = false;
450
if (game->status() == Game::Run)
452
if ( pInput->getBoolProperty("spectator") )
454
else if ( !pInput->getBoolProperty("bankrupt") )
456
printf("may reconnect\n");
457
unsigned int timeout = 180;
458
game->ioInfo("Player has %ds to reconnect until bankruptcy.", timeout);
459
Event *event = newEvent( Event::PlayerTimeout, game );
460
event->setLaunchTime(time(0) + timeout);
461
event->setObject( dynamic_cast<GameObject *> (pInput) );
469
printf("exit from game %d: %d\n", game->id(), pInput->id());
470
exitGame(game, pInput);
471
printf("delplayer %d\n", pInput->id());
476
int MonopdServer::processEvents()
479
gettimeofday(&tv, NULL);
481
int returnvalue = -1;
483
for (std::vector<Event *>::iterator it = m_events.begin() ; it != m_events.end() && (event = *it) ; ++it)
485
if (tv.tv_sec >= event->launchTime())
487
Game *game = event->game();
488
switch(event->type())
490
case Event::TokenMovementTimeout:
493
game->tokenMovementTimeout();
494
if ( game->clientsMoving() )
495
event->setFrequency( 1 );
497
event->setFrequency( 0 );
500
event->setFrequency( 0 );
502
case Event::SocketTimeout:
503
returnvalue = event->id();
505
case Event::AuctionTimeout:
508
unsigned int frequency = game->auctionTimeout();
509
event->setFrequency(frequency);
512
case Event::Monopigator:
513
registerMonopigator();
515
case Event::PlayerTimeout:
516
GameObject *object = event->object();
521
player = findPlayer(object->id());
524
if (player->socket())
527
Game *game = player->game();
528
if (game->status() == Game::Run)
530
game->ioInfo("%s did not reconnect in time and is now bankrupt.", player->name().c_str());
531
game->bankruptPlayer(player);
535
// Game might have ended, silently remove.
536
exitGame(game, player);
537
// Event might have been deleted now.
550
// Delete event from event list
551
int frequency = event->frequency();
553
event->setLaunchTime(time(0) + frequency);
557
break; // can't use continue, damn vectors
568
void MonopdServer::registerMonopigator()
571
struct sockaddr_in sin;
572
struct hostent *hp = gethostbyname(m_gatorHost.c_str());
576
bzero((char *) &sin, sizeof(sin));
577
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
578
sin.sin_family = hp->h_addrtype;
579
sin.sin_port = htons(m_gatorPort);
580
ircsock = socket(AF_INET, SOCK_STREAM, 0);
581
if (connect(ircsock, (struct sockaddr *) & sin, sizeof(sin)))
584
std::string getStr = std::string("GET /register.php?host=") + m_gatorIdentity + "&port=" + itoa(m_port) + "&version=" + escapeHTML(MONOPD_VERSION_STRING) + "&users=" + itoa(m_players.size()) + " HTTP/1.1\nHost:" + m_gatorHost + "\nUser-Agent: monopd/" + MONOPD_VERSION_STRING + "\n\n";
587
write(ircsock, getStr.c_str(), strlen(getStr.c_str()));
592
void MonopdServer::loadConfig()
594
FILE *f = fopen(MONOPD_CONFIGDIR "/monopd.conf", "r");
597
syslog( LOG_WARNING, "cannot open configuration: file=[%s]", MONOPD_CONFIGDIR "/monopd.conf" );
601
char str[1024], *buf;
603
fgets(str, sizeof(str), f);
607
else if (strstr(str, "="))
609
buf = strtok(str, "=");
610
if (!strcmp(buf, "gatorfrequency"))
611
m_gatorFrequency = atoi(strtok(NULL, "\n\0"));
612
else if (!strcmp(buf, "gatorhost"))
613
m_gatorHost = strtok(NULL, "\n\0");
614
else if (!strcmp(buf, "gatoridentity"))
616
m_gatorIdentity = strtok(NULL, "\n\0");
617
m_useMonopigator = true;
619
else if (!strcmp(buf, "port"))
620
setPort(atoi(strtok(NULL, "\n\0")));
621
else if (!strcmp(buf, "gatorport"))
622
m_gatorPort = atoi(strtok(NULL, "\n\0"));
624
fgets(str, sizeof(str), f);
628
if (m_gatorFrequency < 60)
629
m_gatorFrequency = 60;
632
void MonopdServer::loadGameTemplates()
637
struct dirent *direntp;
638
std::string name = "", description = "";
640
dirp = opendir(MONOPD_DATADIR "/games/");
643
syslog( LOG_ERR, "cannot open game directory, dir=[%s]", MONOPD_DATADIR "/games/" );
646
while ((direntp=readdir(dirp)) != NULL)
648
if (strstr(direntp->d_name, ".conf"))
650
std::string filename = std::string(MONOPD_DATADIR) + "/games/" + direntp->d_name;
651
f = fopen(filename.c_str(), "r");
654
syslog( LOG_WARNING, "cannot open game configuration: file=[%s/%s]", MONOPD_DATADIR "/games", filename.c_str() );
658
fgets(str, sizeof(str), f);
662
else if (strstr(str, "="))
664
buf = strtok(str, "=");
665
if (!strcmp(buf, "name"))
666
name = strtok(NULL, "\n\0");
667
else if (!strcmp(buf, "description"))
668
description = strtok(NULL, "\n\0");
670
fgets(str, sizeof(str), f);
674
newGameConfig(strtok(direntp->d_name, "."), (name.size() ? name : strtok(direntp->d_name, ".")), (description.size() ? description : "No description available"));
682
void MonopdServer::initMonopigatorEvent()
684
if (m_useMonopigator==true)
686
// Register Monopigator event
687
m_monopigatorEvent = newEvent(Event::Monopigator);
688
m_monopigatorEvent->setLaunchTime(time(0));
689
m_monopigatorEvent->setFrequency(m_gatorFrequency);
693
void MonopdServer::initSocketTimeoutEvent(int socketFd)
695
Event *socketTimeout = newEvent(Event::SocketTimeout, 0, socketFd);
696
socketTimeout->setLaunchTime(time(0) + 30);
699
void MonopdServer::delSocketTimeoutEvent(int socketFd)
702
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (event = *it) ; ++it)
703
if (event->id() == socketFd)
710
void MonopdServer::ioWrite(const char *fmt, ...)
713
char *buf = new char[size];
714
static std::string ioStr;
722
n = vsnprintf(buf, size, fmt, arg);
725
if (n > -1 && n < size)
739
buf = new char[size];
743
void MonopdServer::ioWrite(const std::string &data, const bool &noGameOnly)
746
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
747
if ( !(noGameOnly && player->game()) )
748
player->ioWrite(data);
751
void MonopdServer::sendGameList(Player *pInput, const bool &sendTemplates)
753
pInput->ioWrite("<monopd>");
755
// Supported game types for new games
756
GameConfig *gcTmp = 0;
758
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin() ; it != m_gameConfigs.end() && (gcTmp = *it) ; ++it )
759
pInput->ioWrite("<gameupdate gameid=\"-1\" gametype=\"%s\" name=\"%s\" description=\"%s\"/>", gcTmp->id().c_str(), gcTmp->name().c_str(), gcTmp->description().c_str());
761
// FIXME: DEPRECATED 1.0
762
pInput->ioWrite("<updategamelist type=\"full\">");
764
// Supported game types for new games (always send, type=full)
766
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin() ; it != m_gameConfigs.end() && (gcTmp = *it) ; ++it )
767
pInput->ioWrite("<game id=\"-1\" gametype=\"%s\" name=\"%s\" description=\"%s\"/>", gcTmp->id().c_str(), gcTmp->name().c_str(), gcTmp->description().c_str());
769
// Games currently under configuration, we can join these
771
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (gTmp = *it) ; ++it)
772
if (gTmp->status() == Game::Config)
773
pInput->ioWrite("<game id=\"%ld\" players=\"%d\" gametype=\"%s\" name=\"%s\" description=\"%s\" canbejoined=\"%d\"/>", gTmp->id(), gTmp->players(), gTmp->gameType().c_str(), gTmp->name().c_str(), gTmp->getStringProperty("description").c_str(), gTmp->getBoolProperty("canbejoined"));
775
pInput->ioWrite("</updategamelist>");
776
// END FIXME: DEPRECATED 1.0
778
pInput->ioWrite("</monopd>\n");
781
void MonopdServer::processInput(Socket *socket, const std::string data)
783
Player *pInput = findPlayer(socket);
786
// The 'n' name command is available even for non-players. In fact,
787
// it's considered to be the protocol handshake.
794
pNew = newPlayer(socket, data.substr(2));
796
sendXMLUpdate(pNew, true, true); // give new player a full update (excluding self) so it knows who's in the lounge
799
pNew = newPlayer(socket, "");
800
reconnectPlayer(pNew, data.substr(2));
809
processCommands(pInput, data.substr(1));
814
if (data.size() > 256)
815
pInput->ioError("Chat messages are limited to 256 characters");
818
if (Game *game = pInput->game())
819
game->ioWrite("<monopd><msg type=\"chat\" playerid=\"%d\" author=\"%s\" value=\"%s\"/></monopd>\n", pInput->id(), pInput->name().c_str(), escapeXML(data).c_str());
821
ioWrite("<monopd><msg type=\"chat\" playerid=\"" + itoa(pInput->id()) + "\" author=\"" + pInput->name() + "\" value=\"" + escapeXML(data) + "\"/></monopd>\n", true);
825
void MonopdServer::processCommands(Player *pInput, const std::string data2)
827
char *data = (char *)data2.c_str();
829
pInput->setRequestedUpdate(false);
831
// The following commands are _always_ available.
835
setPlayerName(pInput, std::string(data+1));
841
pInput->setProperty("image", data+2, this);
847
// Commands available when player is not within a game.
848
Game *game = pInput->game();
857
sendGameList(pInput, true);
860
newGame(pInput, data2.substr(2));
863
joinGame(pInput, atol(data2.substr(2).c_str()));
866
joinGame( pInput, atol(data2.substr(2).c_str()), true );
869
pInput->ioNoSuchCmd("you are not within a game");
873
reconnectPlayer(pInput, data2.substr(1));
876
pInput->ioNoSuchCmd("you are not within a game");
878
// The rest of the commands are only available within a game.
882
// These commands are always available in a running game, no matter what.
886
game->sendFullUpdate(pInput, true);
892
exitGame(game, pInput);
898
switch(game->status())
901
pInput->ioNoSuchCmd("this game has ended");
902
// The rest of the commands are only available when the game has not ended.
907
if (game->status() != Game::Config && !pInput->getBoolProperty("bankrupt") && !pInput->getBoolProperty("spectator"))
915
pInput->updateTradeObject(data+1);
918
pInput->updateTradeMoney(data+2);
921
game->newTrade(pInput, atol(data2.substr(2).c_str()));
924
game->acceptTrade(pInput, data+2);
927
game->rejectTrade(pInput, atol(data2.substr(2).c_str()));
936
game->setTokenLocation(pInput, atoi(data+1));
937
if (!game->clientsMoving())
938
if (Event *event = findEvent(game, Event::TokenMovementTimeout))
943
if (pInput->getBoolProperty("spectator") || pInput->getBoolProperty("bankrupt"))
945
pInput->ioNoSuchCmd("you are only a spectator");
946
// The rest of the commands are only available for participating players
950
if (game->clientsMoving())
952
pInput->ioNoSuchCmd("other clients are still moving");
953
// The rest of the commands are only available when no clients are moving
957
// If we're in a tax dialog, we don't accept too many commands.
958
if (game->pausedForDialog())
969
pInput->payTax(true);
975
// The rest of the commands are not available during a tax dialog
982
case 't': // client authors find the no such command message annoying.
985
// From the official rules: "may buy and erect at any time"
990
pInput->buyHouse(atoi(data+2));
993
pInput->sellHouse(atoi(data+2));
997
// From official rules: "Unimproved properties can be mortgaged
998
// through the Bank at any time"
999
// Selling estates is not officially supported, but it makes most
1005
pInput->mortgageEstate(atoi(data+2));
1008
pInput->sellEstate(atoi(data+2));
1013
// Auctions restrict movement and stuff.
1014
Auction *auction = game->auction();
1015
if (auction && auction->status() != Auction::Completed)
1023
if (!game->bidInAuction(pInput, data+2))
1026
event = findEvent(game, Event::AuctionTimeout);
1028
event = newEvent(Event::AuctionTimeout, game);
1029
event->setLaunchTime(time(0) + 4);
1033
pInput->ioNoSuchCmd();
1037
pInput->ioNoSuchCmd("An auction is in progress.");
1042
// Declaring bankruptcy is only possible when a player is in debt.
1045
if (game->findDebt(pInput))
1049
game->declareBankrupt(pInput);
1052
game->solveDebts(pInput, true);
1055
pInput->ioNoSuchCmd("there are debts to be settled");
1058
pInput->ioNoSuchCmd("there are debts to be settled");
1059
// The rest of the commands are only available when there
1060
// are no debts to be settled.
1064
// These are only available when it's the player's turn
1065
if(pInput->getBoolProperty("hasturn"))
1067
if(pInput->getBoolProperty("can_buyestate"))
1075
pInput->buyEstate();
1078
game->newAuction(pInput);
1081
pInput->ioNoSuchCmd();
1088
if(pInput->getBoolProperty("jailed"))
1096
pInput->useJailCard();
1105
pInput->ioNoSuchCmd();
1111
if(pInput->getBoolProperty("can_roll"))
1117
Event *event = newEvent(Event::TokenMovementTimeout, game);
1118
event->setLaunchTime(time(0) + 10);
1125
// The following commands have their own availability checks.
1129
pInput->endTurn(true);
1135
setGameDescription(pInput, data2.substr(2));
1138
game->editConfiguration( pInput, data+2 );
1141
game->editConfig(pInput, data+2);
1145
pKick = game->kickPlayer( pInput, atoi(data+2) );
1147
exitGame(game, pKick);
1150
game->upgradePlayer( pInput, atoi(data+2) );
1153
// FIXME: DEPRECATED 1.0
1156
game->start(pInput);
1158
// FIXME: DEPRECATED 1.0
1159
if (game->status() == Game::Run)
1160
ioWrite("<monopd><updategamelist type=\"del\"><game id=\"" + itoa(game->id()) + "\"/></updategamelist></monopd>\n");
1163
pInput->ioNoSuchCmd();
1167
pInput->closeSocket();
1170
pInput->ioNoSuchCmd();
1174
void MonopdServer::sendXMLUpdates()
1176
// Send XML to all players
1177
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
1178
sendXMLUpdate((*it));
1180
// Reset propertiesChanged for all player and game objects
1181
// TODO: cache whether they changed above and reuse here?
1182
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
1183
(*it)->unsetPropertiesChanged();
1184
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (*it) ; ++it)
1186
(*it)->unsetPropertiesChanged();
1188
// Let games handle resets for its own objects ..
1189
(*it)->unsetChildProperties();
1193
void MonopdServer::sendXMLUpdate(Player *pOutput, bool fullUpdate, bool excludeSelf)
1195
bool updateEmpty = true;
1197
// Send updates *about* all players ..
1198
Player *pUpdate = 0;
1199
for(std::vector<Player *>::iterator uit = m_players.begin(); uit != m_players.end() && (pUpdate = *uit) ; ++uit)
1201
// .. but only when changed (and in property scope)
1202
if (!excludeSelf || pUpdate != pOutput)
1204
std::string updateXML = pUpdate->oldXMLUpdate(pOutput, fullUpdate);
1205
if (updateXML.size())
1209
pOutput->ioWrite("<monopd> ");
1210
updateEmpty = false;
1212
pOutput->ioWrite("%s", updateXML.c_str());
1217
// Send updates *about* all games ..
1219
for(std::vector<Game *>::iterator uit = m_games.begin(); uit != m_games.end() && (gUpdate = *uit) ; ++uit)
1221
// .. but only when changed (and in property scope)
1222
std::string updateXML = gUpdate->oldXMLUpdate(pOutput, fullUpdate);
1223
if (updateXML.size())
1227
pOutput->ioWrite("<monopd> ");
1228
updateEmpty = false;
1230
pOutput->ioWrite("%s", updateXML.c_str());
1234
// Let game handle updates *about* all of its objects ..
1235
if (Game *game = pOutput->game())
1236
updateEmpty = game->sendChildXMLUpdate(pOutput, updateEmpty);
1239
pOutput->ioWrite("</monopd>\n");
1242
void MonopdServer::setPlayerName(Player *player, const std::string &name)
1244
std::string useName = ( name.size() ? name : "anonymous" );
1247
while (findPlayer(useName))
1248
useName = ( name.size() ? name : "anonymous" ) + itoa( ++i );
1250
player->setProperty("name", useName, this);
1252
Game *game = player->game();
1255
// FIXME: DEPRECATED 1.0
1256
if (game->status() == Game::Config && player == game->master())
1257
ioWrite(std::string("<monopd><updategamelist type=\"edit\"><game id=\"") + itoa(game->id()) + "\" players=\"" + itoa(game->getIntProperty("players")) + "\" gametype=\"" + game->gameType() + "\" name=\"" + game->name() + "\" description=\"" + game->getStringProperty("description") + "\" canbejoined=\"" + itoa(game->getBoolProperty("canbejoined")) + "\"/></updategamelist></monopd>\n");