~ubuntu-branches/debian/sid/monopd/sid

« back to all changes in this revision

Viewing changes to .pc/monopd-0.9.3-dosfix.patch/src/server.cpp

  • Committer: Package Import Robot
  • Author(s): Markus Koschany
  • Date: 2014-02-28 15:39:47 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20140228153947-1a5z05x3alj61wx1
Tags: 0.9.5-1
* Imported Upstream version 0.9.5.
  - Add support for IPv6 and systemd.
* Update watch file and point to the new upstream location
  at tuxfamily.org.
* Drop all patches. Merged upstream.
* debian/control:
  - Drop libcapsinetwork-dev from Build-Depends.
    Essential features of libcapsinetwork were merged into monopd and
    IPv6 support was added.
  - Add pkg-config to Build-Depends.
  - Add libsystemd-daemon-dev [linux-any] to Build-Depends.
  - Update homepage field and point to gtkatlantic.gradator.net.
* Add monopd.service and monopd.socket file and support systemd.
* Update monopd.6 man page.
* Use dh-systemd helper.
* Update debian/copyright. Add new BSD license.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright (c) 2001-2004 Rob Kaper <cap@capsi.com>,
2
 
//               2001 Erik Bourget <ebourg@cs.mcgill.ca>
3
 
//
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.
7
 
//
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.
12
 
//
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.
17
 
 
18
 
#include <dirent.h>
19
 
#include <netdb.h>
20
 
#include <stdio.h>
21
 
#include <stdarg.h>
22
 
#include <sys/time.h>
23
 
#include <sys/socket.h>
24
 
#include <sys/types.h>
25
 
#include <netinet/in.h>
26
 
#include <syslog.h>
27
 
#include <unistd.h>
28
 
#include <stdlib.h>
29
 
#include <string.h>
30
 
 
31
 
#include <string>
32
 
 
33
 
#include <libcapsinetwork/socket.h>
34
 
 
35
 
#include "const.h"
36
 
#include "auction.h"
37
 
#include "event.h"
38
 
#include "game.h"
39
 
#include "gameobject.h"
40
 
#include "gameconfig.h"
41
 
#include "io.h"
42
 
#include "player.h"
43
 
#include "server.h"
44
 
 
45
 
MonopdServer::MonopdServer() : GameObject(0)
46
 
{
47
 
        m_nextGameId = m_nextPlayerId = 1;
48
 
        m_gatorIdentity = "";
49
 
        m_port = 1234;
50
 
        m_gatorHost = "monopd.unixcode.org";
51
 
        m_gatorPort = 80;
52
 
        m_gatorFrequency = 60;
53
 
        m_useMonopigator = false;
54
 
        m_monopigatorEvent = 0;
55
 
 
56
 
        loadConfig();
57
 
        loadGameTemplates();
58
 
}
59
 
 
60
 
void MonopdServer::setPort(int port)
61
 
{
62
 
        m_port = port;
63
 
}
64
 
 
65
 
MonopdServer::~MonopdServer()
66
 
{
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()); }
72
 
}
73
 
 
74
 
Event *MonopdServer::newEvent(unsigned int eventType, Game *game, int id)
75
 
{
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());
79
 
        return newEvent;
80
 
}
81
 
 
82
 
void MonopdServer::delEvent(Event *event)
83
 
{
84
 
        for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
85
 
                if (*it == event)
86
 
                {
87
 
                        printf("delEvent %d/%d\n", event->id(), m_events.size()-1);
88
 
                        delete event;
89
 
                        m_events.erase(it);
90
 
                        break;
91
 
                }
92
 
}
93
 
 
94
 
Event *MonopdServer::findEvent(Game *game, unsigned int eventType)
95
 
{
96
 
        Event *event = 0;
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)
99
 
                        return event;
100
 
 
101
 
        return 0;
102
 
}
103
 
 
104
 
Event *MonopdServer::findEvent(Game *game, GameObject *object)
105
 
{
106
 
        for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
107
 
                if ( (*it)->game() == game && (*it)->object() == object)
108
 
                        return (*it);
109
 
 
110
 
        return 0;
111
 
}
112
 
 
113
 
void MonopdServer::newGame(Player *player, const std::string gameType)
114
 
{
115
 
        Game *game = new Game(m_nextGameId++);
116
 
        m_games.push_back(game);
117
 
 
118
 
        game->setProperty("name", (gameType.size() ? gameType : "atlantic"), this);
119
 
 
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);
127
 
 
128
 
        game->loadGameType(gameType);
129
 
 
130
 
        if (!game->isValid())
131
 
        {
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());
134
 
                m_nextGameId--;
135
 
                return;
136
 
        }
137
 
 
138
 
        syslog( LOG_INFO, "new game: id=[%d], type=[%s], games=[%d]", game->id(), gameType.c_str(), m_games.size() );
139
 
 
140
 
        game->addPlayer(player, true);
141
 
 
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");
144
 
}
145
 
 
146
 
void MonopdServer::joinGame(Player *pInput, unsigned int gameId, const bool &spectator)
147
 
{
148
 
        Game *game = findGame(gameId);
149
 
        if (!game)
150
 
        {
151
 
                pInput->ioError("There is no game with id %ld.", gameId);
152
 
                return;
153
 
        }
154
 
 
155
 
        if ( spectator )
156
 
        {
157
 
                GameObject *config = game->findConfigOption( "allowspectators" );
158
 
                if ( config && config->getBoolProperty( "value" ) && game->status() == Game::Run )
159
 
                {
160
 
                        game->addPlayer( pInput, false, true );
161
 
                        game->ioInfo("%s joins as spectator.", pInput->name().c_str());
162
 
                }
163
 
                else
164
 
                        pInput->ioError("Game %ld doesn't allow spectators.", gameId);
165
 
                return;
166
 
        }
167
 
 
168
 
        int maxPlayers = game->getIntProperty("maxplayers");
169
 
        if (game->players() >= maxPlayers)
170
 
        {
171
 
                pInput->ioError("This game already has the maximum of %d players.", maxPlayers);
172
 
                return;
173
 
        }
174
 
 
175
 
        if (game->status() != Game::Config)
176
 
        {
177
 
                pInput->ioError("You cannot join game %d, it is already in progress.", game->id());
178
 
                return;
179
 
        }
180
 
 
181
 
        game->addPlayer(pInput);
182
 
 
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");
185
 
}
186
 
 
187
 
void MonopdServer::exitGame(Game *game, Player *pInput)
188
 
{
189
 
        game->removePlayer(pInput);
190
 
        if (game->connectedPlayers() == 0)
191
 
                delGame(game);
192
 
 
193
 
        pInput->reset();
194
 
 
195
 
        // Send new game list
196
 
        sendGameList(pInput);
197
 
}
198
 
 
199
 
Game *MonopdServer::findGame(unsigned int gameId)
200
 
{
201
 
        Game *game = 0;
202
 
        for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (game = *it) ; ++it)
203
 
                if (game->id() == gameId)
204
 
                        return game;
205
 
 
206
 
        return 0;
207
 
}
208
 
 
209
 
void MonopdServer::delGame(Game *game, bool verbose)
210
 
{
211
 
        if (verbose)
212
 
                ioWrite("<monopd><deletegame gameid=\"" + itoa(game->id()) + "\"/></monopd>\n");
213
 
 
214
 
        // Remove everyone from the game
215
 
        while (game->players())
216
 
        {
217
 
                Player *player = 0;
218
 
                for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
219
 
                        if (player->game() == game)
220
 
                        {
221
 
                                game->removePlayer(player);
222
 
                                delPlayer(player);
223
 
                                break;
224
 
                        }
225
 
        }
226
 
 
227
 
        // Remove events from game
228
 
        for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (*it) ; ++it)
229
 
        {
230
 
                if ( (*it)->game() == game )
231
 
                {
232
 
                        delEvent( (*it) );
233
 
                        it = m_events.begin();
234
 
                        continue;
235
 
                }
236
 
        }
237
 
 
238
 
        // Remove game
239
 
        for(std::vector<Game *>::iterator it = m_games.begin(); it != m_games.end() && (*it) ; ++it)
240
 
                if (*it == game)
241
 
                {
242
 
                        // FIXME: DEPRECATED 1.0
243
 
                        if (verbose)
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 );
246
 
                        m_games.erase(it);
247
 
                        break;
248
 
                }
249
 
        delete game;
250
 
}
251
 
 
252
 
void MonopdServer::setGameDescription(Player *pInput, const std::string data)
253
 
{
254
 
        Game *game = pInput->game();
255
 
        if (pInput == game->master())
256
 
        {
257
 
                game->setProperty("description", data);
258
 
 
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");
261
 
        }
262
 
        else
263
 
                pInput->ioError("Only the master can set the game description!");
264
 
}
265
 
 
266
 
Player *MonopdServer::newPlayer(Socket *socket, const std::string &name)
267
 
{
268
 
        // Players completed the handshake, delete socket timeout event
269
 
        delSocketTimeoutEvent( socket->fd() );
270
 
 
271
 
        Player *player = new Player(socket, m_nextPlayerId++);
272
 
        m_players.push_back(player);
273
 
        addToScope(player);
274
 
 
275
 
        player->setProperty("game", -1, this);
276
 
        player->setProperty("host", socket->fqdn(), this);
277
 
        setPlayerName(player, name);
278
 
 
279
 
        player->sendClientMsg();
280
 
        sendGameList(player, true);
281
 
 
282
 
        syslog( LOG_INFO, "new player: id=[%d], fd=[%d], name=[%s], players=[%d]", player->id(), socket->fd(), name.c_str(), m_players.size() );
283
 
        player = 0;
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);
286
 
 
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() );
291
 
 
292
 
        return player;
293
 
}
294
 
 
295
 
void MonopdServer::reconnectPlayer(Player *pInput, const std::string &cookie)
296
 
{
297
 
        Player *player = findCookie(cookie);
298
 
        if (!player)
299
 
        {
300
 
                pInput->ioError("Invalid cookie.");
301
 
                return;
302
 
        }
303
 
 
304
 
        Game *game = player->game();
305
 
        if (game)
306
 
        {
307
 
                if (player->socket())
308
 
                        pInput->ioError("Cannot reconnect, target player already has a socket.");
309
 
                else
310
 
                {
311
 
                        pInput->ioInfo("Reconnecting.");
312
 
                        player->setSocket(pInput->socket());
313
 
                        player->sendClientMsg();
314
 
                        sendGameList(player, true);
315
 
                        if (game->status() == Game::Run)
316
 
                        {
317
 
                                game->setStatus(Game::Init);
318
 
                                game->sendFullUpdate(player);
319
 
                                game->setStatus(Game::Run);
320
 
                                game->sendStatus(player);
321
 
                        }
322
 
                        else
323
 
                                game->sendFullUpdate(player);
324
 
 
325
 
                        game->ioInfo("%s reconnected.", player->name().c_str());
326
 
                        pInput->setSocket(0);
327
 
                        delPlayer(pInput);
328
 
                }
329
 
        }
330
 
}
331
 
 
332
 
void MonopdServer::delPlayer(Player *player)
333
 
{
334
 
        // Delete timeout event, if present.
335
 
        Game *game = player ? player->game() : 0;
336
 
        if ( player && game )
337
 
        {
338
 
                Event *event = findEvent( game, dynamic_cast<GameObject *> (player) );
339
 
                if ( event )
340
 
                {
341
 
                        printf("cleared event for player\n");
342
 
                        event->setObject( 0 );
343
 
                }
344
 
                game->removePlayer( player );
345
 
        }
346
 
 
347
 
        ioWrite("<monopd><deleteplayer playerid=\"" + itoa(player->id()) + "\"/></monopd>\n");
348
 
 
349
 
        for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (*it) ; ++it)
350
 
                if (*it == player)
351
 
                {
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);
355
 
                        delete player;
356
 
                        m_players.erase(it);
357
 
                        player = 0;
358
 
                        break;
359
 
                }
360
 
 
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);
363
 
 
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() );
368
 
}
369
 
 
370
 
Player *MonopdServer::findPlayer(int playerId)
371
 
{
372
 
        Player *player = 0;
373
 
        for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
374
 
                if (player->id() == playerId)
375
 
                        return player;
376
 
 
377
 
        return 0;
378
 
}
379
 
 
380
 
Player *MonopdServer::findPlayer(Socket *socket)
381
 
{
382
 
        Player *player = 0;
383
 
        for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
384
 
                if (player->socket() == socket)
385
 
                        return player;
386
 
 
387
 
        return 0;
388
 
}
389
 
 
390
 
Player *MonopdServer::findPlayer(const std::string &name)
391
 
{
392
 
        Player *player = 0;
393
 
        for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
394
 
                if (player->getStringProperty("name") == name)
395
 
                        return player;
396
 
 
397
 
        return 0;
398
 
}
399
 
 
400
 
Player *MonopdServer::findCookie(const std::string &cookie)
401
 
{
402
 
        Player *player = 0;
403
 
        for(std::vector<Player *>::iterator it = m_players.begin(); it != m_players.end() && (player = *it) ; ++it)
404
 
                if (player->getStringProperty("cookie") == cookie)
405
 
                        return player;
406
 
 
407
 
        return 0;
408
 
}
409
 
 
410
 
GameConfig *MonopdServer::newGameConfig(const std::string id, const std::string name, const std::string description)
411
 
{
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;
416
 
}
417
 
 
418
 
void MonopdServer::delGameConfig(GameConfig *gameConfig)
419
 
{
420
 
        for(std::vector<GameConfig *>::iterator it = m_gameConfigs.begin(); it != m_gameConfigs.end() && (*it) ; ++it)
421
 
                if (*it == gameConfig)
422
 
                {
423
 
                        delete gameConfig;
424
 
                        m_gameConfigs.erase(it);
425
 
                        break;
426
 
                }
427
 
}
428
 
 
429
 
void MonopdServer::closedSocket(Socket *socket)
430
 
{
431
 
        Player *pInput = findPlayer(socket);
432
 
        if (!pInput)
433
 
        {
434
 
                // Delete socket timeout event, socket is closed already
435
 
                delSocketTimeoutEvent( socket->fd() );
436
 
                return;
437
 
        }
438
 
 
439
 
        Game *game = pInput->game();
440
 
        if (!game)
441
 
        {
442
 
                delPlayer(pInput);
443
 
                return;
444
 
        }
445
 
 
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());
449
 
        
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)
453
 
        {
454
 
                if ( pInput->getBoolProperty("spectator") )
455
 
                        exitFromGame = true;
456
 
                else if ( !pInput->getBoolProperty("bankrupt") )
457
 
                {
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) );
464
 
                }
465
 
        }
466
 
        else
467
 
                exitFromGame = true;
468
 
        
469
 
        if (exitFromGame)
470
 
        {
471
 
                printf("exit from game %d: %d\n", game->id(), pInput->id());
472
 
                exitGame(game, pInput);
473
 
                printf("delplayer %d\n", pInput->id());
474
 
                delPlayer(pInput);
475
 
        }
476
 
}
477
 
 
478
 
int MonopdServer::processEvents()
479
 
{
480
 
        struct timeval tv;
481
 
        gettimeofday(&tv, NULL);
482
 
 
483
 
        int returnvalue = -1;
484
 
        Event *event = 0;
485
 
        for (std::vector<Event *>::iterator it = m_events.begin() ; it != m_events.end() && (event = *it) ; ++it)
486
 
        {
487
 
                if (tv.tv_sec >= event->launchTime())
488
 
                {
489
 
                        Game *game = event->game();
490
 
                        switch(event->type())
491
 
                        {
492
 
                        case Event::TokenMovementTimeout:
493
 
                                if (game)
494
 
                                {
495
 
                                        game->tokenMovementTimeout();
496
 
                                        if ( game->clientsMoving() )
497
 
                                                event->setFrequency( 1 );
498
 
                                        else
499
 
                                                event->setFrequency( 0 );
500
 
                                }
501
 
                                else
502
 
                                        event->setFrequency( 0 );
503
 
                                break;
504
 
                        case Event::SocketTimeout:
505
 
                                returnvalue = event->id();
506
 
                                break;
507
 
                        case Event::AuctionTimeout:
508
 
                                if (game)
509
 
                                {
510
 
                                        unsigned int frequency = game->auctionTimeout();
511
 
                                        event->setFrequency(frequency);
512
 
                                }
513
 
                                break;
514
 
                        case Event::Monopigator:
515
 
                                registerMonopigator();
516
 
                                break;
517
 
                        case Event::PlayerTimeout:
518
 
                                GameObject *object = event->object();
519
 
                                if (!object)
520
 
                                        break;
521
 
 
522
 
                                Player *player;
523
 
                                player = findPlayer(object->id());
524
 
                                if (player)
525
 
                                {
526
 
                                        if (player->socket())
527
 
                                        break;
528
 
 
529
 
                                        Game *game = player->game();
530
 
                                        if (game->status() == Game::Run)
531
 
                                        {
532
 
                                                game->ioInfo("%s did not reconnect in time and is now bankrupt.", player->name().c_str());
533
 
                                                game->bankruptPlayer(player);
534
 
                                        }
535
 
                                        else
536
 
                                        {
537
 
                                                // Game might have ended, silently remove.
538
 
                                                exitGame(game, player);
539
 
                                                // Event might have been deleted now.
540
 
                                                event = 0;
541
 
 
542
 
                                                delPlayer(player);
543
 
                                        }
544
 
                                }
545
 
                                if ( event )
546
 
                                        event->setObject(0);
547
 
                                break;
548
 
                        }
549
 
 
550
 
                        if ( event )
551
 
                        {
552
 
                                // Delete event from event list
553
 
                                int frequency = event->frequency();
554
 
                                if (frequency)
555
 
                                        event->setLaunchTime(time(0) + frequency);
556
 
                                else
557
 
                                {
558
 
                                        delEvent(event);
559
 
                                        break; // can't use continue, damn vectors
560
 
                                }
561
 
                        }
562
 
                        else
563
 
                                break;
564
 
                } 
565
 
        }
566
 
        sendXMLUpdates();
567
 
        return returnvalue;
568
 
}
569
 
 
570
 
void MonopdServer::registerMonopigator()
571
 
{
572
 
        int ircsock;
573
 
        struct sockaddr_in sin;
574
 
        struct hostent *hp = gethostbyname(m_gatorHost.c_str());        
575
 
        if (!hp)
576
 
                return;
577
 
 
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)))
584
 
                return;
585
 
 
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";
587
 
        if (ircsock)
588
 
        {
589
 
                write(ircsock, getStr.c_str(), strlen(getStr.c_str()));
590
 
                close(ircsock);
591
 
        }
592
 
}
593
 
 
594
 
void MonopdServer::loadConfig()
595
 
{
596
 
        FILE *f = fopen(MONOPD_CONFIGDIR "/monopd.conf", "r");
597
 
        if (!f)
598
 
        {
599
 
                syslog( LOG_WARNING, "cannot open configuration: file=[%s]", MONOPD_CONFIGDIR "/monopd.conf" );
600
 
                return;
601
 
        }
602
 
 
603
 
        char str[1024], *buf;
604
 
 
605
 
        fgets(str, sizeof(str), f);
606
 
        while(!feof(f))
607
 
        {
608
 
                if (str[0]=='#') {}
609
 
                else if (strstr(str, "="))
610
 
                {
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"))
617
 
                        {
618
 
                                m_gatorIdentity = strtok(NULL, "\n\0");
619
 
                                m_useMonopigator = true;
620
 
                        }
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"));
625
 
                }
626
 
                fgets(str, sizeof(str), f);
627
 
        }
628
 
        fclose(f);
629
 
 
630
 
        if (m_gatorFrequency < 60)
631
 
                m_gatorFrequency = 60;
632
 
}
633
 
 
634
 
void MonopdServer::loadGameTemplates()
635
 
{
636
 
        DIR *dirp;
637
 
        FILE *f;
638
 
        char str[256], *buf;
639
 
        struct dirent *direntp;
640
 
        std::string name = "", description = "";
641
 
        
642
 
        dirp = opendir(MONOPD_DATADIR "/games/");
643
 
        if (!dirp)
644
 
        {
645
 
                syslog( LOG_ERR, "cannot open game directory, dir=[%s]", MONOPD_DATADIR "/games/" );
646
 
                return;
647
 
        }
648
 
        while ((direntp=readdir(dirp)) != NULL)
649
 
        {
650
 
                if (strstr(direntp->d_name, ".conf"))
651
 
                {
652
 
                        std::string filename = std::string(MONOPD_DATADIR) + "/games/" + direntp->d_name;
653
 
                        f = fopen(filename.c_str(), "r");
654
 
                        if (!f)
655
 
                        {
656
 
                                syslog( LOG_WARNING, "cannot open game configuration: file=[%s/%s]", MONOPD_DATADIR "/games", filename.c_str() );
657
 
                                continue;
658
 
                        }
659
 
 
660
 
                        fgets(str, sizeof(str), f);
661
 
                        while (!feof(f))
662
 
                        {
663
 
                                if (str[0]=='#') {}
664
 
                                else if (strstr(str, "="))
665
 
                                {
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");
671
 
                                }
672
 
                                fgets(str, sizeof(str), f);
673
 
                        }
674
 
                        fclose(f);
675
 
 
676
 
                        newGameConfig(strtok(direntp->d_name, "."), (name.size() ? name : strtok(direntp->d_name, ".")), (description.size() ? description : "No description available"));
677
 
                        name = "";
678
 
                        description = "";
679
 
                }
680
 
        }
681
 
        closedir(dirp);
682
 
}
683
 
 
684
 
void MonopdServer::initMonopigatorEvent()
685
 
{
686
 
        if (m_useMonopigator==true)
687
 
        {
688
 
                // Register Monopigator event
689
 
                m_monopigatorEvent = newEvent(Event::Monopigator);
690
 
                m_monopigatorEvent->setLaunchTime(time(0));
691
 
                m_monopigatorEvent->setFrequency(m_gatorFrequency);
692
 
        }
693
 
}
694
 
 
695
 
void MonopdServer::initSocketTimeoutEvent(int socketFd)
696
 
{
697
 
                Event *socketTimeout = newEvent(Event::SocketTimeout, 0, socketFd);
698
 
                socketTimeout->setLaunchTime(time(0) + 30);
699
 
}
700
 
 
701
 
void MonopdServer::delSocketTimeoutEvent(int socketFd)
702
 
{
703
 
        Event *event = 0;
704
 
        for(std::vector<Event *>::iterator it = m_events.begin(); it != m_events.end() && (event = *it) ; ++it)
705
 
                if (event->id() == socketFd)
706
 
                {
707
 
                        delEvent(event);
708
 
                        return;
709
 
                }
710
 
}
711
 
 
712
 
void MonopdServer::ioWrite(const char *fmt, ...)
713
 
{
714
 
        int n, size = 256;
715
 
        char *buf = new char[size];
716
 
        static std::string ioStr;
717
 
        va_list arg;
718
 
 
719
 
        buf[0] = 0;
720
 
 
721
 
        while (1)
722
 
        {
723
 
                va_start(arg, fmt);
724
 
                n = vsnprintf(buf, size, fmt, arg);
725
 
                va_end(arg);
726
 
 
727
 
                if (n > -1 && n < size)
728
 
                {
729
 
                        ioStr = buf;
730
 
                        delete[] buf;
731
 
                        ioWrite(ioStr);
732
 
                        return;
733
 
                }
734
 
 
735
 
                if (n > -1)
736
 
                        size = n+1;
737
 
                else
738
 
                        size *= 2;
739
 
 
740
 
                delete[] buf;
741
 
                buf = new char[size];
742
 
        }
743
 
}
744
 
 
745
 
void MonopdServer::ioWrite(const std::string &data, const bool &noGameOnly)
746
 
{
747
 
        Player *player = 0;
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);
751
 
}
752
 
 
753
 
void MonopdServer::sendGameList(Player *pInput, const bool &sendTemplates)
754
 
{
755
 
        pInput->ioWrite("<monopd>");
756
 
 
757
 
        // Supported game types for new games   
758
 
        GameConfig *gcTmp = 0;
759
 
        if (sendTemplates)
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());
762
 
 
763
 
        // FIXME: DEPRECATED 1.0
764
 
        pInput->ioWrite("<updategamelist type=\"full\">");
765
 
 
766
 
        // Supported game types for new games (always send, type=full)
767
 
        gcTmp = 0;
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());
770
 
 
771
 
        // Games currently under configuration, we can join these
772
 
        Game *gTmp = 0;
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"));
776
 
 
777
 
        pInput->ioWrite("</updategamelist>");
778
 
        // END FIXME: DEPRECATED 1.0
779
 
 
780
 
        pInput->ioWrite("</monopd>\n");
781
 
}
782
 
 
783
 
void MonopdServer::processInput(Socket *socket, const std::string data)
784
 
{
785
 
        Player *pInput = findPlayer(socket);
786
 
        if (!pInput)
787
 
        {
788
 
                // The 'n' name command is available even for non-players. In fact,
789
 
                // it's considered to be the protocol handshake.
790
 
                if (data[0] == '.')
791
 
                {
792
 
                        Player *pNew = 0;
793
 
                        switch(data[1])
794
 
                        {
795
 
                        case 'n':
796
 
                                pNew = newPlayer(socket, data.substr(2));
797
 
                                sendXMLUpdates();
798
 
                                sendXMLUpdate(pNew, true, true); // give new player a full update (excluding self) so it knows who's in the lounge
799
 
                                return;
800
 
                        case 'R':
801
 
                                pNew = newPlayer(socket, "");
802
 
                                reconnectPlayer(pNew, data.substr(2));
803
 
                                return;
804
 
                        }
805
 
                }
806
 
                return;
807
 
        }
808
 
 
809
 
        if (data[0] == '.')
810
 
        {
811
 
                processCommands(pInput, data.substr(1));
812
 
                sendXMLUpdates();
813
 
                return;
814
 
        }
815
 
 
816
 
        if (data.size() > 256)
817
 
                pInput->ioError("Chat messages are limited to 256 characters");
818
 
        else
819
 
        {
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());
822
 
                else
823
 
                        ioWrite("<monopd><msg type=\"chat\" playerid=\"" + itoa(pInput->id()) + "\" author=\"" + pInput->name() + "\" value=\"" + escapeXML(data) + "\"/></monopd>\n", true);
824
 
        }
825
 
}
826
 
 
827
 
void MonopdServer::processCommands(Player *pInput, const std::string data2)
828
 
{
829
 
        char *data = (char *)data2.c_str();
830
 
        if (data[0] != 'f')
831
 
                pInput->setRequestedUpdate(false);
832
 
 
833
 
        // The following commands are _always_ available.
834
 
        switch(data[0])
835
 
        {
836
 
        case 'n':
837
 
                setPlayerName(pInput, std::string(data+1));
838
 
                return;
839
 
        case 'p':
840
 
                switch(data[1])
841
 
                {
842
 
                        case 'i':
843
 
                                pInput->setProperty("image", data+2, this);
844
 
                                return;
845
 
                }
846
 
                break;
847
 
        }
848
 
 
849
 
        // Commands available when player is not within a game.
850
 
        Game *game = pInput->game();
851
 
        if (!game)
852
 
        {
853
 
                switch(data[0])
854
 
                {
855
 
                case 'g':
856
 
                        switch(data[1])
857
 
                        {
858
 
                                case 'l':
859
 
                                        sendGameList(pInput, true);
860
 
                                        return;
861
 
                                case 'n':
862
 
                                        newGame(pInput, data2.substr(2));
863
 
                                        return;
864
 
                                case 'j':
865
 
                                        joinGame(pInput, atol(data2.substr(2).c_str()));
866
 
                                        return;
867
 
                                case 'S':
868
 
                                        joinGame( pInput, atol(data2.substr(2).c_str()), true );
869
 
                                        return;
870
 
                                default:
871
 
                                        pInput->ioNoSuchCmd("you are not within a game");
872
 
                        }
873
 
                        break;
874
 
                case 'R':
875
 
                        reconnectPlayer(pInput, data2.substr(1));
876
 
                        break;
877
 
                default:
878
 
                        pInput->ioNoSuchCmd("you are not within a game");
879
 
                }
880
 
                // The rest of the commands are only available within a game.
881
 
                return;
882
 
        }
883
 
 
884
 
        // These commands are always available in a running game, no matter what.
885
 
        switch(data[0])
886
 
        {
887
 
        case 'f':
888
 
                game->sendFullUpdate(pInput, true);
889
 
                return;
890
 
        case 'g':
891
 
                switch(data[1])
892
 
                {
893
 
                case 'x':
894
 
                        exitGame(game, pInput);
895
 
                        return;
896
 
                }
897
 
                break;
898
 
        }
899
 
 
900
 
        switch(game->status())
901
 
        {
902
 
        case Game::End:
903
 
                pInput->ioNoSuchCmd("this game has ended");
904
 
                // The rest of the commands are only available when the game has not ended.
905
 
                return;
906
 
        default:;
907
 
        }
908
 
 
909
 
        if (game->status() != Game::Config && !pInput->getBoolProperty("bankrupt") && !pInput->getBoolProperty("spectator"))
910
 
                switch(data[0])
911
 
                {
912
 
                        case 'T':
913
 
                                switch(data[1])
914
 
                                {
915
 
                                case 'c':
916
 
                                case 'e':
917
 
                                        pInput->updateTradeObject(data+1);
918
 
                                        return;
919
 
                                case 'm':
920
 
                                        pInput->updateTradeMoney(data+2);
921
 
                                        return;
922
 
                                case 'n':
923
 
                                        game->newTrade(pInput, atol(data2.substr(2).c_str()));
924
 
                                        return;
925
 
                                case 'a':
926
 
                                        game->acceptTrade(pInput, data+2);
927
 
                                        return;
928
 
                                case 'r':
929
 
                                        game->rejectTrade(pInput, atol(data2.substr(2).c_str()));
930
 
                                        return;
931
 
                                }
932
 
                                break;
933
 
                }
934
 
 
935
 
        switch(data[0])
936
 
        {
937
 
        case 't':
938
 
                game->setTokenLocation(pInput, atoi(data+1));
939
 
                if (!game->clientsMoving())
940
 
                        if (Event *event = findEvent(game, Event::TokenMovementTimeout))
941
 
                                delEvent(event);
942
 
                return;
943
 
        }
944
 
 
945
 
        if (pInput->getBoolProperty("spectator") || pInput->getBoolProperty("bankrupt"))
946
 
        {
947
 
                pInput->ioNoSuchCmd("you are only a spectator");
948
 
                // The rest of the commands are only available for participating players
949
 
                return;
950
 
        }
951
 
 
952
 
        if (game->clientsMoving())
953
 
        {
954
 
                pInput->ioNoSuchCmd("other clients are still moving");
955
 
                // The rest of the commands are only available when no clients are moving
956
 
                return;
957
 
        }
958
 
 
959
 
        // If we're in a tax dialog, we don't accept too many commands.
960
 
        if (game->pausedForDialog())
961
 
        {
962
 
                switch(data[0])
963
 
                {
964
 
                case 'T':
965
 
                        switch(data[1])
966
 
                        {
967
 
                        case '$':
968
 
                                pInput->payTax();
969
 
                                return;
970
 
                        case '%':
971
 
                                pInput->payTax(true);
972
 
                                return;
973
 
                        default:
974
 
                                return;
975
 
                        }
976
 
                default:
977
 
                        // The rest of the commands are not available during a tax dialog
978
 
                        return;
979
 
                }
980
 
        }
981
 
 
982
 
        switch(data[0])
983
 
        {
984
 
                case 't': // client authors find the no such command message annoying.
985
 
                        return;
986
 
                break;
987
 
                // From the official rules: "may buy and erect at any time"
988
 
                case 'h':
989
 
                        switch(data[1])
990
 
                        {
991
 
                                case 'b':
992
 
                                        pInput->buyHouse(atoi(data+2));
993
 
                                        return;
994
 
                                case 's':
995
 
                                        pInput->sellHouse(atoi(data+2));
996
 
                                        return;
997
 
                        }
998
 
                        break;
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
1002
 
                // sense here.
1003
 
                case 'e':
1004
 
                        switch(data[1])
1005
 
                        {
1006
 
                                case 'm':
1007
 
                                        pInput->mortgageEstate(atoi(data+2));
1008
 
                                        return;
1009
 
                                case 's':
1010
 
                                        pInput->sellEstate(atoi(data+2));
1011
 
                                        return;
1012
 
                        }
1013
 
        }
1014
 
 
1015
 
        // Auctions restrict movement and stuff.
1016
 
        Auction *auction = game->auction();
1017
 
        if (auction && auction->status() != Auction::Completed)
1018
 
        {
1019
 
                switch(data[0])
1020
 
                        {
1021
 
                        case 'a':
1022
 
                                switch(data[1])
1023
 
                                {
1024
 
                                case 'b':
1025
 
                                        if (!game->bidInAuction(pInput, data+2))
1026
 
                                        {
1027
 
                                                Event *event;
1028
 
                                                event = findEvent(game, Event::AuctionTimeout);
1029
 
                                                if (!event)
1030
 
                                                        event = newEvent(Event::AuctionTimeout, game);
1031
 
                                                event->setLaunchTime(time(0) + 4);
1032
 
                                        }
1033
 
                                        return;
1034
 
                                default:
1035
 
                                        pInput->ioNoSuchCmd();
1036
 
                                        return;
1037
 
                                }
1038
 
                        default:
1039
 
                                pInput->ioNoSuchCmd("An auction is in progress.");
1040
 
                                return;
1041
 
                        }
1042
 
        }
1043
 
 
1044
 
        // Declaring bankruptcy is only possible when a player is in debt.
1045
 
        if (game->debts())
1046
 
        {
1047
 
                if (game->findDebt(pInput))
1048
 
                        switch(data[0])
1049
 
                        {
1050
 
                                case 'D':
1051
 
                                        game->declareBankrupt(pInput);
1052
 
                                        break;
1053
 
                                case 'p':
1054
 
                                        game->solveDebts(pInput, true);
1055
 
                                        break;
1056
 
                                default:
1057
 
                                        pInput->ioNoSuchCmd("there are debts to be settled");
1058
 
                        }
1059
 
                else
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.
1063
 
                return;
1064
 
        }
1065
 
 
1066
 
        // These are only available when it's the player's turn
1067
 
        if(pInput->getBoolProperty("hasturn"))
1068
 
        {
1069
 
                if(pInput->getBoolProperty("can_buyestate"))
1070
 
                {
1071
 
                        switch(data[0]) 
1072
 
                        {
1073
 
                        case 'e':
1074
 
                                switch(data[1])
1075
 
                                {
1076
 
                                case 'b':
1077
 
                                        pInput->buyEstate();
1078
 
                                        return;
1079
 
                        case 'a':
1080
 
                                        game->newAuction(pInput);
1081
 
                                        return;
1082
 
                                default:
1083
 
                                        pInput->ioNoSuchCmd();
1084
 
                                        return;
1085
 
                                }
1086
 
                                break;
1087
 
                        }
1088
 
                }
1089
 
                
1090
 
                if(pInput->getBoolProperty("jailed"))
1091
 
                {
1092
 
                        switch(data[0]) 
1093
 
                        {
1094
 
                                case 'j':
1095
 
                                        switch(data[1])
1096
 
                                        {
1097
 
                                                case 'c':
1098
 
                                                        pInput->useJailCard();
1099
 
                                                        return;
1100
 
                                                case 'p':
1101
 
                                                        pInput->payJail();
1102
 
                                                        return;
1103
 
                                                case 'r':
1104
 
                                                        pInput->rollJail();
1105
 
                                                        return;
1106
 
                                                default:
1107
 
                                                        pInput->ioNoSuchCmd();
1108
 
                                        }
1109
 
                                        break;
1110
 
                        }
1111
 
                }
1112
 
                
1113
 
                if(pInput->getBoolProperty("can_roll"))
1114
 
                {
1115
 
                        switch(data[0]) 
1116
 
                        {
1117
 
                                case 'r':
1118
 
                                        pInput->rollDice();
1119
 
                                        Event *event = newEvent(Event::TokenMovementTimeout, game);
1120
 
                                        event->setLaunchTime(time(0) + 10);
1121
 
                                        return;
1122
 
                        }
1123
 
                        
1124
 
                }
1125
 
        }
1126
 
 
1127
 
        // The following commands have their own availability checks.
1128
 
        switch(data[0]) 
1129
 
        {
1130
 
                case 'E':
1131
 
                        pInput->endTurn(true);
1132
 
                        break;
1133
 
                case 'g':
1134
 
                        switch(data[1])
1135
 
                        {
1136
 
                                case 'd':
1137
 
                                        setGameDescription(pInput, data2.substr(2));
1138
 
                                        return;
1139
 
                                case 'c':
1140
 
                                        game->editConfiguration( pInput, data+2 );
1141
 
                                        return;
1142
 
                                case 'e':
1143
 
                                        game->editConfig(pInput, data+2);
1144
 
                                        return;
1145
 
                                case 'k':
1146
 
                                        Player *pKick;
1147
 
                                        pKick = game->kickPlayer( pInput, atoi(data+2) );
1148
 
                                        if (pKick)
1149
 
                                                exitGame(game, pKick);
1150
 
                                        return;
1151
 
                                case 'u':
1152
 
                                        game->upgradePlayer( pInput, atoi(data+2) );
1153
 
                                        return;
1154
 
                                case 'p':
1155
 
                                        // FIXME: DEPRECATED 1.0
1156
 
                                        return;
1157
 
                                case 's':
1158
 
                                        game->start(pInput);
1159
 
 
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");
1163
 
                                        return;
1164
 
                                default:
1165
 
                                        pInput->ioNoSuchCmd();
1166
 
                        }
1167
 
                        break;
1168
 
                case 'd':
1169
 
                        pInput->closeSocket();
1170
 
                        return;
1171
 
                default:
1172
 
                        pInput->ioNoSuchCmd();
1173
 
        }
1174
 
}
1175
 
 
1176
 
void MonopdServer::sendXMLUpdates()
1177
 
{
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));
1181
 
 
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)
1187
 
        {
1188
 
                (*it)->unsetPropertiesChanged();
1189
 
 
1190
 
                // Let games handle resets for its own objects ..
1191
 
                (*it)->unsetChildProperties();
1192
 
        }
1193
 
}
1194
 
 
1195
 
void MonopdServer::sendXMLUpdate(Player *pOutput, bool fullUpdate, bool excludeSelf)
1196
 
{
1197
 
        bool updateEmpty = true;
1198
 
 
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)
1202
 
        {
1203
 
                // .. but only when changed (and in property scope)
1204
 
                if (!excludeSelf || pUpdate != pOutput)
1205
 
                {
1206
 
                        std::string updateXML = pUpdate->oldXMLUpdate(pOutput, fullUpdate);
1207
 
                        if (updateXML.size())
1208
 
                        {
1209
 
                                if (updateEmpty)
1210
 
                                {
1211
 
                                        pOutput->ioWrite("<monopd> ");
1212
 
                                        updateEmpty = false;
1213
 
                                }
1214
 
                                pOutput->ioWrite("%s", updateXML.c_str());
1215
 
                        }
1216
 
                }
1217
 
        }
1218
 
 
1219
 
        // Send updates *about* all games ..
1220
 
        Game *gUpdate = 0;
1221
 
        for(std::vector<Game *>::iterator uit = m_games.begin(); uit != m_games.end() && (gUpdate = *uit) ; ++uit)
1222
 
        {
1223
 
                // .. but only when changed (and in property scope)
1224
 
                std::string updateXML = gUpdate->oldXMLUpdate(pOutput, fullUpdate);
1225
 
                if (updateXML.size())
1226
 
                {
1227
 
                        if (updateEmpty)
1228
 
                        {
1229
 
                                pOutput->ioWrite("<monopd> ");
1230
 
                                updateEmpty = false;
1231
 
                        }
1232
 
                        pOutput->ioWrite("%s", updateXML.c_str());
1233
 
                }
1234
 
        }
1235
 
 
1236
 
        // Let game handle updates *about* all of its objects ..
1237
 
        if (Game *game = pOutput->game())
1238
 
                updateEmpty = game->sendChildXMLUpdate(pOutput, updateEmpty);
1239
 
        
1240
 
        if (!updateEmpty)
1241
 
                pOutput->ioWrite("</monopd>\n");
1242
 
}
1243
 
 
1244
 
void MonopdServer::setPlayerName(Player *player, const std::string &name)
1245
 
{
1246
 
                std::string useName = ( name.size() ? name : "anonymous" );
1247
 
 
1248
 
                int i=1;
1249
 
                while (findPlayer(useName))
1250
 
                        useName = ( name.size() ? name : "anonymous" ) + itoa( ++i );
1251
 
 
1252
 
                player->setProperty("name", useName, this);
1253
 
 
1254
 
                Game *game = player->game();
1255
 
                if (game)
1256
 
                {
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");
1260
 
                }
1261
 
}