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>
33
#include <libcapsinetwork/socket.h>
39
#include "gameobject.h"
40
#include "gameconfig.h"
45
MonopdServer::MonopdServer() : GameObject(0)
47
m_nextGameId = m_nextPlayerId = 1;
50
m_gatorHost = "monopd.unixcode.org";
52
m_gatorFrequency = 60;
53
m_useMonopigator = false;
54
m_monopigatorEvent = 0;
60
void MonopdServer::setPort(int port)
65
MonopdServer::~MonopdServer()
67
// We are responsible for the objects allocated
68
while (!m_events.empty()) { delete *m_events.begin(); m_events.erase(m_events.begin()); }
69
while (!m_games.empty()) { delete *m_games.begin(); m_games.erase(m_games.begin()); }
70
while (!m_gameConfigs.empty()) { delete *m_gameConfigs.begin(); m_gameConfigs.erase(m_gameConfigs.begin()); }
71
while (!m_players.empty()) { delete *m_players.begin(); m_players.erase(m_players.begin()); }
74
Event *MonopdServer::newEvent(unsigned int eventType, Game *game, int id)
76
Event *newEvent = new Event(id, (Event::EventType)eventType, game);
77
m_events.push_back(newEvent);
78
printf("newEvent %d/%d\n", id, m_events.size());
82
void MonopdServer::delEvent(Event *event)
84
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
87
printf("delEvent %d/%d\n", event->id(), m_events.size()-1);
94
Event *MonopdServer::findEvent(Game *game, unsigned int eventType)
97
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (event = *it) ; ++it)
98
if (event->game() == game && event->type() == eventType)
104
Event *MonopdServer::findEvent(Game *game, GameObject *object)
106
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
107
if ( (*it)->game() == game && (*it)->object() == object)
113
void MonopdServer::newGame(Player *player, const std::string gameType)
115
Game *game = new Game(m_nextGameId++);
116
m_games.push_back(game);
118
game->setProperty("name", (gameType.size() ? gameType : "atlantic"), this);
120
// Hardcoded reasonable defaults, which also ensure the entire server is the scope
121
game->setProperty("master", -1, this); // addPlayer will set the correct playerid
122
game->setProperty("description", "", this);
123
game->setProperty("minplayers", 1, this);
124
game->setProperty("maxplayers", 1, this);
125
game->setProperty("players", 0, this);
126
game->setBoolProperty("canbejoined", true, this);
128
game->loadGameType(gameType);
130
if (!game->isValid())
132
delGame(game, false);
133
player->ioWrite("<monopd><msg type=\"error\" value=\"Could not load valid configuration for gametype %s.\"/></monopd>\n", gameType.c_str());
138
syslog( LOG_INFO, "new game: id=[%d], type=[%s], games=[%d]", game->id(), gameType.c_str(), m_games.size() );
140
game->addPlayer(player, true);
142
// FIXME: DEPRECATED 1.0
143
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");
146
void MonopdServer::joinGame(Player *pInput, unsigned int gameId, const bool &spectator)
148
Game *game = findGame(gameId);
151
pInput->ioError("There is no game with id %ld.", gameId);
157
GameObject *config = game->findConfigOption( "allowspectators" );
158
if ( config && config->getBoolProperty( "value" ) && game->status() == Game::Run )
160
game->addPlayer( pInput, false, true );
161
game->ioInfo("%s joins as spectator.", pInput->name().c_str());
164
pInput->ioError("Game %ld doesn't allow spectators.", gameId);
168
int maxPlayers = game->getIntProperty("maxplayers");
169
if (game->players() >= maxPlayers)
171
pInput->ioError("This game already has the maximum of %d players.", maxPlayers);
175
if (game->status() != Game::Config)
177
pInput->ioError("You cannot join game %d, it is already in progress.", game->id());
181
game->addPlayer(pInput);
183
// FIXME: DEPRECATED 1.0
184
ioWrite(std::string("<monopd><updategamelist type=\"edit\"><game id=\"") + itoa(game->id()) + "\" players=\"" + itoa(game->players()) + "\" canbejoined=\"" + itoa(game->getBoolProperty("canbejoined")) + "\"/></updategamelist></monopd>\n");
187
void MonopdServer::exitGame(Game *game, Player *pInput)
189
game->removePlayer(pInput);
190
if (game->connectedPlayers() == 0)
195
// Send new game list
196
sendGameList(pInput);
199
Game *MonopdServer::findGame(unsigned int gameId)
202
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (game = *it) ; ++it)
203
if (game->id() == gameId)
209
void MonopdServer::delGame(Game *game, bool verbose)
212
ioWrite("<monopd><deletegame gameid=\"" + itoa(game->id()) + "\"/></monopd>\n");
214
// Remove everyone from the game
215
while (game->players())
218
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
219
if (player->game() == game)
221
game->removePlayer(player);
227
// Remove events from game
228
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
230
if ( (*it)->game() == game )
233
it = m_events.begin();
239
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (*it) ; ++it)
242
// FIXME: DEPRECATED 1.0
244
ioWrite(std::string("<monopd><updategamelist type=\"del\"><game id=\"") + itoa(game->id()) + "\"/></updategamelist></monopd>\n");
245
syslog( LOG_INFO, "del game: id=[%d], games=[%d]", game->id(), m_games.size() - 1 );
252
void MonopdServer::setGameDescription(Player *pInput, const std::string data)
254
Game *game = pInput->game();
255
if (pInput == game->master())
257
game->setProperty("description", data);
259
// FIXME: DEPRECATED 1.0
260
ioWrite("<monopd><updategamelist type=\"edit\"><game id=\"" + itoa(game->id()) + "\" gametype=\"" + game->gameType() + "\" name=\"" + game->name() + "\" description=\"" + game->getStringProperty("description") + "\"/></updategamelist></monopd>\n");
263
pInput->ioError("Only the master can set the game description!");
266
Player *MonopdServer::newPlayer(Socket *socket, const std::string &name)
268
// Players completed the handshake, delete socket timeout event
269
delSocketTimeoutEvent( socket->fd() );
271
Player *player = new Player(socket, m_nextPlayerId++);
272
m_players.push_back(player);
275
player->setProperty("game", -1, this);
276
player->setProperty("host", socket->fqdn(), this);
277
setPlayerName(player, name);
279
player->sendClientMsg();
280
sendGameList(player, true);
282
syslog( LOG_INFO, "new player: id=[%d], fd=[%d], name=[%s], players=[%d]", player->id(), socket->fd(), name.c_str(), m_players.size() );
284
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
285
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);
287
// Re-register to meta server with updated player count.
288
registerMonopigator();
289
if (m_monopigatorEvent)
290
m_monopigatorEvent->setLaunchTime(time(0) + m_monopigatorEvent->frequency() );
295
void MonopdServer::reconnectPlayer(Player *pInput, const std::string &cookie)
297
Player *player = findCookie(cookie);
300
pInput->ioError("Invalid cookie.");
304
Game *game = player->game();
307
if (player->socket())
308
pInput->ioError("Cannot reconnect, target player already has a socket.");
311
pInput->ioInfo("Reconnecting.");
312
player->setSocket(pInput->socket());
313
player->sendClientMsg();
314
sendGameList(player, true);
315
if (game->status() == Game::Run)
317
game->setStatus(Game::Init);
318
game->sendFullUpdate(player);
319
game->setStatus(Game::Run);
320
game->sendStatus(player);
323
game->sendFullUpdate(player);
325
game->ioInfo("%s reconnected.", player->name().c_str());
326
pInput->setSocket(0);
332
void MonopdServer::delPlayer(Player *player)
334
// Delete timeout event, if present.
335
Game *game = player ? player->game() : 0;
336
if ( player && game )
338
Event *event = findEvent( game, dynamic_cast<GameObject *> (player) );
341
printf("cleared event for player\n");
342
event->setObject( 0 );
344
game->removePlayer( player );
347
ioWrite("<monopd><deleteplayer playerid=\"" + itoa(player->id()) + "\"/></monopd>\n");
349
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
352
removeFromScope(player);
353
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 );
354
printf("delPlayer %d/%d\n", player->id(), m_players.size()-1);
361
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
362
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);
364
// Re-register to meta server with updated player count.
365
registerMonopigator();
366
if (m_monopigatorEvent)
367
m_monopigatorEvent->setLaunchTime(time(0) + m_monopigatorEvent->frequency() );
370
Player *MonopdServer::findPlayer(int playerId)
373
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
374
if (player->id() == playerId)
380
Player *MonopdServer::findPlayer(Socket *socket)
383
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
384
if (player->socket() == socket)
390
Player *MonopdServer::findPlayer(const std::string &name)
393
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
394
if (player->getStringProperty("name") == name)
400
Player *MonopdServer::findCookie(const std::string &cookie)
403
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
404
if (player->getStringProperty("cookie") == cookie)
410
GameConfig *MonopdServer::newGameConfig(const std::string id, const std::string name, const std::string description)
412
GameConfig *newGameConfig = new GameConfig(id, name, description);
413
m_gameConfigs.push_back(newGameConfig);
414
syslog( LOG_INFO, "loaded game configuration: game=[%s]", name.c_str() );
415
return newGameConfig;
418
void MonopdServer::delGameConfig(GameConfig *gameConfig)
420
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin(); it != m_gameConfigs.end() && (*it) ; ++it)
421
if (*it == gameConfig)
424
m_gameConfigs.erase(it);
429
void MonopdServer::closedSocket(Socket *socket)
431
Player *pInput = findPlayer(socket);
434
// Delete socket timeout event, socket is closed already
435
delSocketTimeoutEvent( socket->fd() );
439
Game *game = pInput->game();
446
pInput->setSocket( 0 );
447
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 );
448
game->ioInfo("Connection with %s lost.", pInput->name().c_str());
450
// Only remove from game when game not running, or when it's merely a spectator.
451
bool exitFromGame = false;
452
if (game->status() == Game::Run)
454
if ( pInput->getBoolProperty("spectator") )
456
else if ( !pInput->getBoolProperty("bankrupt") )
458
printf("may reconnect\n");
459
unsigned int timeout = 180;
460
game->ioInfo("Player has %ds to reconnect until bankruptcy.", timeout);
461
Event *event = newEvent( Event::PlayerTimeout, game );
462
event->setLaunchTime(time(0) + timeout);
463
event->setObject( dynamic_cast<GameObject *> (pInput) );
471
printf("exit from game %d: %d\n", game->id(), pInput->id());
472
exitGame(game, pInput);
473
printf("delplayer %d\n", pInput->id());
478
int MonopdServer::processEvents()
481
gettimeofday(&tv, NULL);
483
int returnvalue = -1;
485
for (std::vector<Event *>::iterator it = m_events.begin() ; it != m_events.end() && (event = *it) ; ++it)
487
if (tv.tv_sec >= event->launchTime())
489
Game *game = event->game();
490
switch(event->type())
492
case Event::TokenMovementTimeout:
495
game->tokenMovementTimeout();
496
if ( game->clientsMoving() )
497
event->setFrequency( 1 );
499
event->setFrequency( 0 );
502
event->setFrequency( 0 );
504
case Event::SocketTimeout:
505
returnvalue = event->id();
507
case Event::AuctionTimeout:
510
unsigned int frequency = game->auctionTimeout();
511
event->setFrequency(frequency);
514
case Event::Monopigator:
515
registerMonopigator();
517
case Event::PlayerTimeout:
518
GameObject *object = event->object();
523
player = findPlayer(object->id());
526
if (player->socket())
529
Game *game = player->game();
530
if (game->status() == Game::Run)
532
game->ioInfo("%s did not reconnect in time and is now bankrupt.", player->name().c_str());
533
game->bankruptPlayer(player);
537
// Game might have ended, silently remove.
538
exitGame(game, player);
539
// Event might have been deleted now.
552
// Delete event from event list
553
int frequency = event->frequency();
555
event->setLaunchTime(time(0) + frequency);
559
break; // can't use continue, damn vectors
570
void MonopdServer::registerMonopigator()
573
struct sockaddr_in sin;
574
struct hostent *hp = gethostbyname(m_gatorHost.c_str());
578
bzero((char *) &sin, sizeof(sin));
579
bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
580
sin.sin_family = hp->h_addrtype;
581
sin.sin_port = htons(m_gatorPort);
582
ircsock = socket(AF_INET, SOCK_STREAM, 0);
583
if (connect(ircsock, (struct sockaddr *) & sin, sizeof(sin)))
586
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";
589
write(ircsock, getStr.c_str(), strlen(getStr.c_str()));
594
void MonopdServer::loadConfig()
596
FILE *f = fopen(MONOPD_CONFIGDIR "/monopd.conf", "r");
599
syslog( LOG_WARNING, "cannot open configuration: file=[%s]", MONOPD_CONFIGDIR "/monopd.conf" );
603
char str[1024], *buf;
605
fgets(str, sizeof(str), f);
609
else if (strstr(str, "="))
611
buf = strtok(str, "=");
612
if (!strcmp(buf, "gatorfrequency"))
613
m_gatorFrequency = atoi(strtok(NULL, "\n\0"));
614
else if (!strcmp(buf, "gatorhost"))
615
m_gatorHost = strtok(NULL, "\n\0");
616
else if (!strcmp(buf, "gatoridentity"))
618
m_gatorIdentity = strtok(NULL, "\n\0");
619
m_useMonopigator = true;
621
else if (!strcmp(buf, "port"))
622
setPort(atoi(strtok(NULL, "\n\0")));
623
else if (!strcmp(buf, "gatorport"))
624
m_gatorPort = atoi(strtok(NULL, "\n\0"));
626
fgets(str, sizeof(str), f);
630
if (m_gatorFrequency < 60)
631
m_gatorFrequency = 60;
634
void MonopdServer::loadGameTemplates()
639
struct dirent *direntp;
640
std::string name = "", description = "";
642
dirp = opendir(MONOPD_DATADIR "/games/");
645
syslog( LOG_ERR, "cannot open game directory, dir=[%s]", MONOPD_DATADIR "/games/" );
648
while ((direntp=readdir(dirp)) != NULL)
650
if (strstr(direntp->d_name, ".conf"))
652
std::string filename = std::string(MONOPD_DATADIR) + "/games/" + direntp->d_name;
653
f = fopen(filename.c_str(), "r");
656
syslog( LOG_WARNING, "cannot open game configuration: file=[%s/%s]", MONOPD_DATADIR "/games", filename.c_str() );
660
fgets(str, sizeof(str), f);
664
else if (strstr(str, "="))
666
buf = strtok(str, "=");
667
if (!strcmp(buf, "name"))
668
name = strtok(NULL, "\n\0");
669
else if (!strcmp(buf, "description"))
670
description = strtok(NULL, "\n\0");
672
fgets(str, sizeof(str), f);
676
newGameConfig(strtok(direntp->d_name, "."), (name.size() ? name : strtok(direntp->d_name, ".")), (description.size() ? description : "No description available"));
684
void MonopdServer::initMonopigatorEvent()
686
if (m_useMonopigator==true)
688
// Register Monopigator event
689
m_monopigatorEvent = newEvent(Event::Monopigator);
690
m_monopigatorEvent->setLaunchTime(time(0));
691
m_monopigatorEvent->setFrequency(m_gatorFrequency);
695
void MonopdServer::initSocketTimeoutEvent(int socketFd)
697
Event *socketTimeout = newEvent(Event::SocketTimeout, 0, socketFd);
698
socketTimeout->setLaunchTime(time(0) + 30);
701
void MonopdServer::delSocketTimeoutEvent(int socketFd)
704
for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (event = *it) ; ++it)
705
if (event->id() == socketFd)
712
void MonopdServer::ioWrite(const char *fmt, ...)
715
char *buf = new char[size];
716
static std::string ioStr;
724
n = vsnprintf(buf, size, fmt, arg);
727
if (n > -1 && n < size)
741
buf = new char[size];
745
void MonopdServer::ioWrite(const std::string &data, const bool &noGameOnly)
748
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
749
if ( !(noGameOnly && player->game()) )
750
player->ioWrite(data);
753
void MonopdServer::sendGameList(Player *pInput, const bool &sendTemplates)
755
pInput->ioWrite("<monopd>");
757
// Supported game types for new games
758
GameConfig *gcTmp = 0;
760
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin() ; it != m_gameConfigs.end() && (gcTmp = *it) ; ++it )
761
pInput->ioWrite("<gameupdate gameid=\"-1\" gametype=\"%s\" name=\"%s\" description=\"%s\"/>", gcTmp->id().c_str(), gcTmp->name().c_str(), gcTmp->description().c_str());
763
// FIXME: DEPRECATED 1.0
764
pInput->ioWrite("<updategamelist type=\"full\">");
766
// Supported game types for new games (always send, type=full)
768
for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin() ; it != m_gameConfigs.end() && (gcTmp = *it) ; ++it )
769
pInput->ioWrite("<game id=\"-1\" gametype=\"%s\" name=\"%s\" description=\"%s\"/>", gcTmp->id().c_str(), gcTmp->name().c_str(), gcTmp->description().c_str());
771
// Games currently under configuration, we can join these
773
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (gTmp = *it) ; ++it)
774
if (gTmp->status() == Game::Config)
775
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"));
777
pInput->ioWrite("</updategamelist>");
778
// END FIXME: DEPRECATED 1.0
780
pInput->ioWrite("</monopd>\n");
783
void MonopdServer::processInput(Socket *socket, const std::string data)
785
Player *pInput = findPlayer(socket);
788
// The 'n' name command is available even for non-players. In fact,
789
// it's considered to be the protocol handshake.
796
pNew = newPlayer(socket, data.substr(2));
798
sendXMLUpdate(pNew, true, true); // give new player a full update (excluding self) so it knows who's in the lounge
801
pNew = newPlayer(socket, "");
802
reconnectPlayer(pNew, data.substr(2));
811
processCommands(pInput, data.substr(1));
816
if (data.size() > 256)
817
pInput->ioError("Chat messages are limited to 256 characters");
820
if (Game *game = pInput->game())
821
game->ioWrite("<monopd><msg type=\"chat\" playerid=\"%d\" author=\"%s\" value=\"%s\"/></monopd>\n", pInput->id(), pInput->name().c_str(), escapeXML(data).c_str());
823
ioWrite("<monopd><msg type=\"chat\" playerid=\"" + itoa(pInput->id()) + "\" author=\"" + pInput->name() + "\" value=\"" + escapeXML(data) + "\"/></monopd>\n", true);
827
void MonopdServer::processCommands(Player *pInput, const std::string data2)
829
char *data = (char *)data2.c_str();
831
pInput->setRequestedUpdate(false);
833
// The following commands are _always_ available.
837
setPlayerName(pInput, std::string(data+1));
843
pInput->setProperty("image", data+2, this);
849
// Commands available when player is not within a game.
850
Game *game = pInput->game();
859
sendGameList(pInput, true);
862
newGame(pInput, data2.substr(2));
865
joinGame(pInput, atol(data2.substr(2).c_str()));
868
joinGame( pInput, atol(data2.substr(2).c_str()), true );
871
pInput->ioNoSuchCmd("you are not within a game");
875
reconnectPlayer(pInput, data2.substr(1));
878
pInput->ioNoSuchCmd("you are not within a game");
880
// The rest of the commands are only available within a game.
884
// These commands are always available in a running game, no matter what.
888
game->sendFullUpdate(pInput, true);
894
exitGame(game, pInput);
900
switch(game->status())
903
pInput->ioNoSuchCmd("this game has ended");
904
// The rest of the commands are only available when the game has not ended.
909
if (game->status() != Game::Config && !pInput->getBoolProperty("bankrupt") && !pInput->getBoolProperty("spectator"))
917
pInput->updateTradeObject(data+1);
920
pInput->updateTradeMoney(data+2);
923
game->newTrade(pInput, atol(data2.substr(2).c_str()));
926
game->acceptTrade(pInput, data+2);
929
game->rejectTrade(pInput, atol(data2.substr(2).c_str()));
938
game->setTokenLocation(pInput, atoi(data+1));
939
if (!game->clientsMoving())
940
if (Event *event = findEvent(game, Event::TokenMovementTimeout))
945
if (pInput->getBoolProperty("spectator") || pInput->getBoolProperty("bankrupt"))
947
pInput->ioNoSuchCmd("you are only a spectator");
948
// The rest of the commands are only available for participating players
952
if (game->clientsMoving())
954
pInput->ioNoSuchCmd("other clients are still moving");
955
// The rest of the commands are only available when no clients are moving
959
// If we're in a tax dialog, we don't accept too many commands.
960
if (game->pausedForDialog())
971
pInput->payTax(true);
977
// The rest of the commands are not available during a tax dialog
984
case 't': // client authors find the no such command message annoying.
987
// From the official rules: "may buy and erect at any time"
992
pInput->buyHouse(atoi(data+2));
995
pInput->sellHouse(atoi(data+2));
999
// From official rules: "Unimproved properties can be mortgaged
1000
// through the Bank at any time"
1001
// Selling estates is not officially supported, but it makes most
1007
pInput->mortgageEstate(atoi(data+2));
1010
pInput->sellEstate(atoi(data+2));
1015
// Auctions restrict movement and stuff.
1016
Auction *auction = game->auction();
1017
if (auction && auction->status() != Auction::Completed)
1025
if (!game->bidInAuction(pInput, data+2))
1028
event = findEvent(game, Event::AuctionTimeout);
1030
event = newEvent(Event::AuctionTimeout, game);
1031
event->setLaunchTime(time(0) + 4);
1035
pInput->ioNoSuchCmd();
1039
pInput->ioNoSuchCmd("An auction is in progress.");
1044
// Declaring bankruptcy is only possible when a player is in debt.
1047
if (game->findDebt(pInput))
1051
game->declareBankrupt(pInput);
1054
game->solveDebts(pInput, true);
1057
pInput->ioNoSuchCmd("there are debts to be settled");
1060
pInput->ioNoSuchCmd("there are debts to be settled");
1061
// The rest of the commands are only available when there
1062
// are no debts to be settled.
1066
// These are only available when it's the player's turn
1067
if(pInput->getBoolProperty("hasturn"))
1069
if(pInput->getBoolProperty("can_buyestate"))
1077
pInput->buyEstate();
1080
game->newAuction(pInput);
1083
pInput->ioNoSuchCmd();
1090
if(pInput->getBoolProperty("jailed"))
1098
pInput->useJailCard();
1107
pInput->ioNoSuchCmd();
1113
if(pInput->getBoolProperty("can_roll"))
1119
Event *event = newEvent(Event::TokenMovementTimeout, game);
1120
event->setLaunchTime(time(0) + 10);
1127
// The following commands have their own availability checks.
1131
pInput->endTurn(true);
1137
setGameDescription(pInput, data2.substr(2));
1140
game->editConfiguration( pInput, data+2 );
1143
game->editConfig(pInput, data+2);
1147
pKick = game->kickPlayer( pInput, atoi(data+2) );
1149
exitGame(game, pKick);
1152
game->upgradePlayer( pInput, atoi(data+2) );
1155
// FIXME: DEPRECATED 1.0
1158
game->start(pInput);
1160
// FIXME: DEPRECATED 1.0
1161
if (game->status() == Game::Run)
1162
ioWrite("<monopd><updategamelist type=\"del\"><game id=\"" + itoa(game->id()) + "\"/></updategamelist></monopd>\n");
1165
pInput->ioNoSuchCmd();
1169
pInput->closeSocket();
1172
pInput->ioNoSuchCmd();
1176
void MonopdServer::sendXMLUpdates()
1178
// Send XML to all players
1179
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
1180
sendXMLUpdate((*it));
1182
// Reset propertiesChanged for all player and game objects
1183
// TODO: cache whether they changed above and reuse here?
1184
for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
1185
(*it)->unsetPropertiesChanged();
1186
for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (*it) ; ++it)
1188
(*it)->unsetPropertiesChanged();
1190
// Let games handle resets for its own objects ..
1191
(*it)->unsetChildProperties();
1195
void MonopdServer::sendXMLUpdate(Player *pOutput, bool fullUpdate, bool excludeSelf)
1197
bool updateEmpty = true;
1199
// Send updates *about* all players ..
1200
Player *pUpdate = 0;
1201
for(std::vector<Player *>::iterator uit = m_players.begin(); uit != m_players.end() && (pUpdate = *uit) ; ++uit)
1203
// .. but only when changed (and in property scope)
1204
if (!excludeSelf || pUpdate != pOutput)
1206
std::string updateXML = pUpdate->oldXMLUpdate(pOutput, fullUpdate);
1207
if (updateXML.size())
1211
pOutput->ioWrite("<monopd> ");
1212
updateEmpty = false;
1214
pOutput->ioWrite("%s", updateXML.c_str());
1219
// Send updates *about* all games ..
1221
for(std::vector<Game *>::iterator uit = m_games.begin(); uit != m_games.end() && (gUpdate = *uit) ; ++uit)
1223
// .. but only when changed (and in property scope)
1224
std::string updateXML = gUpdate->oldXMLUpdate(pOutput, fullUpdate);
1225
if (updateXML.size())
1229
pOutput->ioWrite("<monopd> ");
1230
updateEmpty = false;
1232
pOutput->ioWrite("%s", updateXML.c_str());
1236
// Let game handle updates *about* all of its objects ..
1237
if (Game *game = pOutput->game())
1238
updateEmpty = game->sendChildXMLUpdate(pOutput, updateEmpty);
1241
pOutput->ioWrite("</monopd>\n");
1244
void MonopdServer::setPlayerName(Player *player, const std::string &name)
1246
std::string useName = ( name.size() ? name : "anonymous" );
1249
while (findPlayer(useName))
1250
useName = ( name.size() ? name : "anonymous" ) + itoa( ++i );
1252
player->setProperty("name", useName, this);
1254
Game *game = player->game();
1257
// FIXME: DEPRECATED 1.0
1258
if (game->status() == Game::Config && player == game->master())
1259
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");