1 /*
2  *  metaserver_messages.h - TCPMess message types for metaserver client
3 
4 	Copyright (C) 2004 and beyond by Woody Zenfell, III
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21  April 15, 2004 (Woody Zenfell):
22 	Created.
23  */
24 
25 #ifndef METASERVER_MESSAGES_H
26 #define METASERVER_MESSAGES_H
27 
28 #include "Message.h"
29 
30 #include "AStream.h"
31 #include "SDL_net.h"
32 #include "Scenario.h" // for scenario name and ID
33 #include "network.h" // for network protocol ID
34 
35 #include <string>
36 #include <vector>
37 #include <boost/algorithm/string/case_conv.hpp>
38 
39 using boost::algorithm::to_lower_copy;
40 
41 enum
42 {
43 	// Message types sent from servers to clients
44 	kSERVER_ROOMLIST	= 0,
45 	kSERVER_PLAYERLIST	= 1,
46 	kSERVER_GAMELIST	= 2,
47 	kSERVER_DENY		= 3,
48 	kSERVER_SALT		= 6,
49 	kSERVER_LOGINSUCCESS	= 7,
50 	kSERVER_SETPLAYERDATA	= 8,
51 	kSERVER_LIMIT		= 9,
52 	kSERVER_BROADCAST	= 10,
53 	kSERVER_ACCEPT		= 12,
54 	kSERVER_FIND		= 14,
55 	kSERVER_STATS		= 17,
56 
57 	// Messages sent from clients to servers
58 	kCLIENT_LOGIN		= 100,
59 	kCLIENT_ROOM_LOGIN	= 101,
60 	kCLIENT_LOGOUT          = 102,
61 	kCLIENT_NAME_TEAM	= 103,
62 	kCLIENT_CREATEGAME	= 104,
63 	kCLIENT_REMOVEGAME	= 105,
64 	kCLIENT_PLAYERMODE      = 107,
65 	kCLIENT_KEY		= 109,
66 	kCLIENT_SYNCGAMES	= 110,
67         kCLIENT_GAMEPLAYERLIST	= 111,
68         kCLIENT_GAMESCORE	= 112,
69 	kCLIENT_RESETGAME	= 113,
70 	kCLIENT_STARTGAME	= 114,
71 	kCLIENT_LOCALIZE	= 115,
72 	kCLIENT_FIND		= 117,
73 	kCLIENT_STATS		= 121,
74 
75 	// Message types sent in both directions
76 	kBOTH_CHAT		= 200,
77 	kBOTH_PRIVATE_MESSAGE	= 201,
78 	kBOTH_KEEP_ALIVE	= 202,
79 };
80 
81 
82 
83 typedef uint8 HandoffToken[32];
84 
85 AIStream& operator >>(AIStream& stream, HandoffToken& token);
86 // Damned if I can convince the linker this operator exists . . .
87 //AOStream& operator <<(AOStream& stream, const HandoffToken& token);
88 
89 
90 
91 struct GameDescription
92 {
93 	uint16		m_type;
94 	int32		m_timeLimit;
95 	uint32		m_mapChecksum;
96 	uint16		m_difficulty;
97 	uint16		m_maxPlayers;
98 	bool		m_teamsAllowed;
99 	bool		m_closed;
100 	bool		m_running;
101 	uint8		m_numPlayers;
102 	std::string	m_name;
103 	std::string	m_mapName;
104 
105 	// future versions will take action on these:
106 	std::string m_scenarioID;
107 	std::string m_networkSetupProtocolID;
108 
109 	// while these are purely for display purposes
110 	std::string m_scenarioName;
111 	std::string m_scenarioVersion;
112 	std::string m_alephoneBuildString;
113 	std::string m_netScript;
114 
115 	// more stuff
116 	bool m_hasGameOptions;
117 	int16 m_gameOptions;
118 	int16 m_cheatFlags;
119 	int16 m_killLimit;
120 	std::string m_mapFileName;
121 	std::string m_physicsName;
122 
GameDescriptionGameDescription123 	GameDescription()
124 		: m_type(0)
125 		, m_timeLimit(0)
126 		, m_mapChecksum(0)
127 		, m_difficulty(0)
128 		, m_maxPlayers(8)
129 		, m_teamsAllowed(false)
130 		, m_closed(false)
131 		, m_running(false)
132 		, m_numPlayers(1)
133 		, m_name("Untitled Game")
134 		, m_mapName("Unspecified Map")
135 		, m_scenarioID(Scenario::instance()->GetID())
136 		, m_networkSetupProtocolID(kNetworkSetupProtocolID)
137 		, m_scenarioName(Scenario::instance()->GetName())
138 		, m_scenarioVersion(Scenario::instance()->GetVersion())
139 		, m_hasGameOptions(false)
140 		, m_gameOptions(0)
141 		, m_cheatFlags(0)
142 		, m_killLimit(0)
143 	{}
144 
145 };
146 
147 AIStream& operator >>(AIStream& stream, GameDescription& desc);
148 AOStream& operator <<(AOStream& stream, const GameDescription& desc);
149 std::ostream& operator <<(std::ostream& stream, const GameDescription& desc);
150 
151 
152 
153 class LoginAndPlayerInfoMessage : public SmallMessageHelper
154 {
155 public:
LoginAndPlayerInfoMessage(const std::string & userName,const std::string & playerName,const std::string & teamName)156 	LoginAndPlayerInfoMessage(const std::string& userName, const std::string& playerName, const std::string& teamName)
157 		: m_userName(userName), m_playerName(playerName), m_teamName(teamName) {}
158 
159 	enum { kType = kCLIENT_LOGIN };
160 
type()161 	MessageTypeID type() const { return kType; }
162 
clone()163 	LoginAndPlayerInfoMessage* clone() const
164 	{ return new LoginAndPlayerInfoMessage(*this); }
165 
166 protected:
167 	void reallyDeflateTo(AOStream& thePacket) const;
168 
reallyInflateFrom(AIStream & inStream)169 	bool reallyInflateFrom(AIStream& inStream)
170 	{
171 		// no support for receiving this type of message
172 		return false;
173 	}
174 
175 private:
176 	std::string	m_userName;
177 	std::string	m_playerName;
178 	std::string	m_teamName;
179 };
180 
181 typedef DatalessMessage<kCLIENT_LOGOUT> LogoutMessage;
182 
183 class SetPlayerModeMessage : public SmallMessageHelper
184 {
185 public:
186 	enum { kType = kCLIENT_PLAYERMODE };
187 
type()188 	MessageTypeID type() const { return kType; }
189 
clone()190 	SetPlayerModeMessage* clone() const
191 		{ return new SetPlayerModeMessage(*this); }
192 
SetPlayerModeMessage(uint16 mode,const std::string & session_id)193 	SetPlayerModeMessage(uint16 mode, const std::string& session_id) : m_mode(mode), m_session_id(session_id) { }
194 
195 protected:
reallyDeflateTo(AOStream & thePacket)196 	void reallyDeflateTo(AOStream& thePacket) const
197 		{
198 			thePacket << m_mode;
199 			thePacket.write(const_cast<char*>(m_session_id.c_str()), m_session_id.size());
200 		}
201 
reallyInflateFrom(AIStream & inStream)202 	bool reallyInflateFrom(AIStream& inStream)
203 		{ assert(false); }
204 
205 private:
206 	uint16 m_mode;
207 	std::string m_session_id;
208 };
209 
210 class SaltMessage : public SmallMessageHelper
211 {
212 public:
213 	enum { kType = kSERVER_SALT, kKeyLength = 16 };
214 
215 	enum { kPlaintextEncryption, kBraindeadSimpleEncryption, kHTTPSEncryption = 4 };
216 
encryptionType()217 	uint16 encryptionType() const { return m_encryptionType; }
salt()218 	const uint8* salt() const { return &(m_salt[0]); }
219 
type()220 	MessageTypeID type() const { return kType; }
221 
clone()222 	SaltMessage* clone() const
223 	{ return new SaltMessage(*this); }
224 
225 protected:
reallyDeflateTo(AOStream & thePacket)226 	void reallyDeflateTo(AOStream& thePacket) const
227 	{
228 		// no need for deflation
229 		assert(false);
230 	}
231 
232 	bool reallyInflateFrom(AIStream& inStream);
233 
234 private:
235 	uint16	m_encryptionType;
236 	uint8	m_salt[kKeyLength];
237 };
238 
239 
240 
241 typedef DatalessMessage<kSERVER_ACCEPT> AcceptMessage;
242 
243 
244 
245 class LocalizationMessage : public SmallMessageHelper
246 {
247 public:
248 	enum { kType = kCLIENT_LOCALIZE };
249 
type()250 	MessageTypeID type() const { return kType; }
251 
clone()252 	LocalizationMessage* clone() const
253 	{ return new LocalizationMessage(*this); }
254 
255 protected:
256 	void reallyDeflateTo(AOStream& thePacket) const;
257 
reallyInflateFrom(AIStream & inStream)258 	bool reallyInflateFrom(AIStream& inStream)
259 	{
260 		// no need for inflation
261 		return false;
262 	}
263 };
264 
265 
266 
267 class RoomDescription
268 {
269 public:
270 	enum
271 	{
272 		kNormalRoomType = 0,
273 		kRankedRoomType,
274 		kTournamentRoomType
275 	};
276 
277 	void read(AIStream& inStream);
278 
279 	const std::string roomName() const;
playerCount()280 	int playerCount() const { return m_playerCount; }
gameCount()281 	int gameCount() const { return m_gameCount; }
roomType()282 	int roomType() const { return m_type; }
roomServerAddress()283 	const IPaddress roomServerAddress() const { return m_address; }
284 
285 private:
286 	uint16 m_id;
287 	uint16 m_playerCount;
288 	uint16 m_gameCount;
289 	uint16 m_type;
290 	IPaddress m_address;
291 };
292 
293 std::ostream& operator <<(std::ostream& out, const RoomDescription& roomDesc);
294 
295 class RoomListMessage : public SmallMessageHelper
296 {
297 public:
298 	enum { kType = kSERVER_ROOMLIST };
299 
type()300 	MessageTypeID type() const { return kType; }
301 
clone()302 	RoomListMessage* clone() const
303 	{ return new RoomListMessage(*this); }
304 
rooms()305 	const std::vector<RoomDescription>& rooms() const { return m_rooms; }
306 
307 protected:
reallyDeflateTo(AOStream & thePacket)308 	void reallyDeflateTo(AOStream& thePacket) const
309 	{
310 		// no need for deflation
311 		assert(false);
312 	}
313 
314 	bool reallyInflateFrom(AIStream& inStream);
315 
316 private:
317 	std::vector<RoomDescription>	m_rooms;
318 };
319 
320 std::ostream& operator <<(std::ostream& out, const RoomListMessage& message);
321 
322 
323 
324 class RoomLoginMessage : public SmallMessageHelper
325 {
326 public:
327 	enum { kType = kCLIENT_ROOM_LOGIN, kKeyLength = 16 };
328 
type()329 	MessageTypeID type() const { return kType; }
330 
clone()331 	RoomLoginMessage* clone() const
332 	{ return new RoomLoginMessage(*this); }
333 
RoomLoginMessage(const std::string & login,const HandoffToken & token)334 	RoomLoginMessage(const std::string& login, const HandoffToken& token) : m_loginName(login)
335 	{
336 		memcpy(m_token, token, sizeof(m_token));
337 	}
338 
339 protected:
340 	void reallyDeflateTo(AOStream& out) const;
341 
reallyInflateFrom(AIStream &)342 	bool reallyInflateFrom(AIStream&)
343 	{
344 		// don't need to be able to receive these
345 		return false;
346 	}
347 
348 private:
349 	HandoffToken	m_token;
350 	std::string	m_loginName;
351 };
352 
353 
354 
355 class NameAndTeamMessage : public SmallMessageHelper
356 {
357 public:
358 	enum { kType = kCLIENT_NAME_TEAM };
359 
type()360 	MessageTypeID type() const { return kType; }
361 
clone()362 	NameAndTeamMessage* clone() const
363 	{ return new NameAndTeamMessage(*this); }
364 
NameAndTeamMessage(const std::string & name,const std::string & team)365 	NameAndTeamMessage(const std::string& name, const std::string& team) : m_name(name), m_team(team), m_away(false), m_away_message("") {}
366 
NameAndTeamMessage(const std::string & name,const std::string & team,bool away,const std::string & away_message)367 	NameAndTeamMessage(const std::string& name, const std::string& team, bool away, const std::string& away_message) : m_name(name), m_team(team), m_away(away), m_away_message(away_message) { }
368 
369 protected:
370 	void reallyDeflateTo(AOStream& out) const;
371 
reallyInflateFrom(AIStream &)372 	bool reallyInflateFrom(AIStream&)
373 	{
374 		// don't need to be able to receive these
375 		return false;
376 	}
377 
378 private:
379 	std::string	m_name;
380 	std::string	m_team;
381 
382 	bool            m_away;
383 	std::string     m_away_message;
384 };
385 
386 class IDAndLimitMessage : public SmallMessageHelper
387 {
388 public:
389 	enum { kType = kSERVER_LIMIT };
390 
playerID()391 	uint32 playerID() const { return m_playerID; }
playerLimit()392 	uint16 playerLimit() const { return m_playerLimit; }
393 
type()394 	MessageTypeID type() const { return kType; }
395 
clone()396 	IDAndLimitMessage* clone() const
397 	{ return new IDAndLimitMessage(*this); }
398 
399 protected:
reallyDeflateTo(AOStream & thePacket)400 	void reallyDeflateTo(AOStream& thePacket) const
401 	{
402 		// no need for deflation
403 		assert(false);
404 	}
405 
406 	bool reallyInflateFrom(AIStream& inStream);
407 
408 private:
409 	uint32	m_playerID;
410 	uint16	m_playerLimit;
411 };
412 
413 
414 
415 class DenialMessage : public SmallMessageHelper
416 {
417 public:
418 	enum { kType = kSERVER_DENY };
419 
type()420 	MessageTypeID type() const { return kType; }
421 
code()422 	uint32 code() const { return m_code; }
message()423 	const std::string message() const { return m_message; }
424 
clone()425 	DenialMessage* clone() const
426 	{ return new DenialMessage(*this); }
427 
428 protected:
reallyDeflateTo(AOStream & thePacket)429 	void reallyDeflateTo(AOStream& thePacket) const
430 	{
431 		// no need for deflation
432 		assert(false);
433 	}
434 
435 	bool reallyInflateFrom(AIStream& inStream);
436 
437 private:
438 	uint32		m_code;
439 	std::string	m_message;
440 };
441 
442 
443 
444 class BroadcastMessage : public SmallMessageHelper
445 {
446 public:
447 	enum { kType = kSERVER_BROADCAST };
448 
type()449 	MessageTypeID type() const { return kType; }
450 
message()451 	const std::string message() const { return m_message; }
452 
clone()453 	BroadcastMessage* clone() const
454 	{ return new BroadcastMessage(*this); }
455 
456 protected:
reallyDeflateTo(AOStream & thePacket)457 	void reallyDeflateTo(AOStream& thePacket) const
458 	{
459 		// no need for deflation
460 		assert(false);
461 	}
462 
463 	bool reallyInflateFrom(AIStream& inStream);
464 
465 private:
466 	std::string m_message;
467 };
468 
469 class PrivateMessage : public SmallMessageHelper
470 {
471 public:
472 	enum { kType = kBOTH_PRIVATE_MESSAGE };
473 	static const int kDirectedBit = 0x1;
474 
type()475 	MessageTypeID type() const { return kType; }
476 
PrivateMessage()477 	PrivateMessage() {}
478 
479 	PrivateMessage(uint32 inSenderID, const std::string& inSenderName, uint32 inSelectedID, const std::string& inMessage);
480 
senderID()481 	const uint32 senderID() const { return m_senderID; }
selectedID()482 	const uint32 selectedID() const { return m_selectedID; }
internalType()483 	const uint16 internalType() const { return m_internalType; }
senderName()484 	const std::string senderName() const { return m_senderName; }
message()485 	const std::string message() const { return m_message; }
directed()486 	const bool directed() const { return m_flags & kDirectedBit; }
487 
clone()488 	PrivateMessage* clone() const
489 		{ return new PrivateMessage(*this); }
490 
491 protected:
492 	void reallyDeflateTo(AOStream& thePacket) const;
493 	bool reallyInflateFrom(AIStream& inStream);
494 
495 private:
496 	uint16 m_color[3] = {};
497 	uint32 m_senderID = 0;
498 	uint32 m_selectedID = 0;
499 	uint16 m_internalType = 0;
500 	uint16 m_flags = 0;
501 	std::string m_senderName;
502 	std::string m_message;
503 };
504 
505 
506 class ChatMessage : public SmallMessageHelper
507 {
508 public:
509 	enum { kType = kBOTH_CHAT };
510 
511 	static const int kDirectedBit = 0x1;
512 
type()513 	MessageTypeID type() const { return kType; }
514 
ChatMessage()515 	ChatMessage() {}
516 
517 	ChatMessage(uint32 inSenderID, const std::string& inSenderName, const std::string& inMessage);
518 
senderID()519 	const uint32 senderID() const { return m_senderID; }
internalType()520 	const uint16 internalType() const { return m_internalType; }
senderName()521 	const std::string senderName() const { return m_senderName; }
message()522 	const std::string message() const { return m_message; }
directed()523 	const bool directed() const { return m_flags & kDirectedBit; }
524 
clone()525 	ChatMessage* clone() const
526 	{ return new ChatMessage(*this); }
527 
528 protected:
529 	void reallyDeflateTo(AOStream& thePacket) const;
530 	bool reallyInflateFrom(AIStream& inStream);
531 
532 private:
533 	uint16 m_color[3] = {};
534 	uint32 m_senderID = 0;
535 	uint16 m_internalType = 0;
536 	uint16 m_flags = 0;
537 	std::string	m_senderName;
538 	std::string	m_message;
539 };
540 
541 
542 
543 typedef DatalessMessage<kBOTH_KEEP_ALIVE> KeepAliveMessage;
544 
545 
546 
547 class MetaserverPlayerInfo
548 {
549 public:
550 	// Flags for adminFlags
551 	static const uint16	kNotAdmin	= 0x0;
552 	static const uint16	kBungie		= 0x1;
553 	static const uint16	kAdmin		= 0x4;
554 
555 	MetaserverPlayerInfo(AIStream& fromStream);
556 
verb()557 	uint16 verb() const { return m_verb; }
playerID()558 	uint32 playerID() const { return m_playerID; }
name()559 	const std::string& name() const { return m_name; }
560 
color()561 	const uint16 *color() const { return m_primaryColor; }
team_color()562 	const uint16 *team_color() const { return m_secondaryColor; }
563 
sort(const MetaserverPlayerInfo & a,const MetaserverPlayerInfo & b)564 	static bool sort(const MetaserverPlayerInfo& a, const MetaserverPlayerInfo& b) {
565 		return (a.m_adminFlags == b.m_adminFlags) ? ( (a.m_status == b.m_status) ? a.playerID() < b.playerID() : a.m_status < b.m_status) : a.m_adminFlags > b.m_adminFlags;
566 	}
567 
568 	friend std::ostream& operator <<(std::ostream& out, const MetaserverPlayerInfo& info);
569 
away()570 	bool away() const { return m_status & 0x1; }
571 
572 	// Conformance to metaserver-maintained-list interface
573 	typedef uint32 IDType;
574 	static const IDType IdNone = 0xffffffff;
575 
id()576 	IDType id() const { return playerID(); }
577 
target()578 	bool target() const { return m_target; }
target(bool _target)579 	void target(bool _target) { m_target = _target; }
580 
581 private:
582 	MetaserverPlayerInfo();
583 
584 	uint16		m_verb;
585 	uint16		m_adminFlags;
586 	uint32		m_ranking;
587 	uint32		m_playerID;
588 	uint32		m_roomID;
589 	uint16		m_rank;
590 	uint16		m_playerDataSize;
591 	uint8		m_icon;
592 	uint8		m_status;
593 	uint16		m_primaryColor[3];
594 	uint16		m_secondaryColor[3];
595 	std::string	m_name;
596 	std::string	m_team;
597 
598 	bool m_target;
599 };
600 
601 class PlayerListMessage : public SmallMessageHelper
602 {
603 public:
604 	enum { kType = kSERVER_PLAYERLIST };
605 
type()606 	MessageTypeID type() const { return kType; }
607 
PlayerListMessage()608 	PlayerListMessage() {}
609 
clone()610 	PlayerListMessage* clone() const
611 	{ return new PlayerListMessage(*this); }
612 
players()613 	const std::vector<MetaserverPlayerInfo>& players() const { return m_players; }
614 
615 
616 protected:
reallyDeflateTo(AOStream & thePacket)617 	void reallyDeflateTo(AOStream& thePacket) const
618 	{
619 		// no need for deflation
620 		assert(false);
621 	}
622 
623 	bool reallyInflateFrom(AIStream& inStream);
624 
625 private:
626 	std::vector<MetaserverPlayerInfo>	m_players;
627 };
628 
629 std::ostream& operator <<(std::ostream& out, const PlayerListMessage& info);
630 
631 
632 
633 class CreateGameMessage : public SmallMessageHelper
634 {
635 public:
636 	enum { kType = kCLIENT_CREATEGAME };
637 
type()638 	MessageTypeID type() const { return kType; }
639 
CreateGameMessage(uint16 gamePort,const GameDescription & description)640 	CreateGameMessage(uint16 gamePort, const GameDescription& description)
641 		: m_gamePort(gamePort)
642 		, m_description(description)
643 	{}
644 
clone()645 	CreateGameMessage* clone() const
646 	{ return new CreateGameMessage(*this); }
647 
648 
649 protected:
650 	void reallyDeflateTo(AOStream& thePacket) const;
651 
reallyInflateFrom(AIStream & inStream)652 	bool reallyInflateFrom(AIStream& inStream)
653 	{
654 		// no need for inflation
655 		assert(false);
656 		return false;
657 	}
658 
659 
660 private:
661 	uint16		m_gamePort;
662 	GameDescription	m_description;
663 };
664 
665 
666 
667 typedef DatalessMessage<kCLIENT_SYNCGAMES> SyncGamesMessage;
668 
669 
670 
671 class LoginSuccessfulMessage : public SmallMessageHelper
672 {
673 public:
674 	enum { kType = kSERVER_LOGINSUCCESS };
675 
type()676 	MessageTypeID type() const { return kType; }
677 
clone()678 	LoginSuccessfulMessage* clone() const
679 	{ return new LoginSuccessfulMessage(*this); }
680 
userID()681 	uint32 userID() const { return m_userID; }
token()682 	const HandoffToken& token() const { return m_token; }
683 
684 protected:
reallyDeflateTo(AOStream & thePacket)685 	void reallyDeflateTo(AOStream& thePacket) const
686 	{
687 		// no need for deflation
688 		assert(false);
689 	}
690 
691 	bool reallyInflateFrom(AIStream& inStream);
692 
693 private:
694 	uint32		m_userID;
695 	uint16		m_order;
696 	HandoffToken	m_token;
697 };
698 
699 
700 
701 class SetPlayerDataMessage : public SmallMessageHelper
702 {
703 public:
704 	enum { kType = kSERVER_SETPLAYERDATA };
705 
type()706 	MessageTypeID type() const { return kType; }
707 
clone()708 	SetPlayerDataMessage* clone() const
709 	{ return new SetPlayerDataMessage(*this); }
710 
711 
712 protected:
reallyDeflateTo(AOStream & thePacket)713 	void reallyDeflateTo(AOStream& thePacket) const
714 	{
715 		// no need for deflation
716 		assert(false);
717 	}
718 
719 	bool reallyInflateFrom(AIStream& inStream);
720 
721 private:
722 };
723 
724 
725 
726 class GameListMessage : public SmallMessageHelper
727 {
728 public:
729 	struct GameListEntry
730 	{
731 		// Conformance to MetaserverMaintainedList's Element interface
732 		typedef uint32 IDType;
733 		static const IDType IdNone = 0xffffffff;
idGameListEntry734 		IDType id() const { return m_gameID; }
verbGameListEntry735 		uint8 verb() const { return m_verb; }
736 
targetGameListEntry737 		bool target() const { return m_target; }
targetGameListEntry738 		void target(bool _target) { m_target = _target; }
739 
runningGameListEntry740 		bool running() const { return m_description.m_running; }
compatibleGameListEntry741 		bool compatible() const { return Scenario::instance()->IsCompatible(m_description.m_scenarioID); }
742 
sortGameListEntry743 		static bool sort(const GameListEntry& a, const GameListEntry& b) {
744 			// sort compatible/not running, then compatible/running
745 			// then not compatible
746 			// in those groups, sort by ID
747 			if (a.compatible() && b.compatible())
748 			{
749 				if (a.running() == b.running())
750 				{
751 					return a.id() < b.id();
752 				}
753 				else
754 				{
755 					return !a.running();
756 				}
757 			}
758 			else if (a.compatible() == b.compatible())
759 			{
760 				return a.id() < b.id();
761 			}
762 			else
763 				return a.compatible();
764 		}
765 
minutes_remainingGameListEntry766 		int minutes_remaining() const {
767 			if (m_timeRemaining == -1) return -1;
768 			int remaining = m_timeRemaining / 60 - (SDL_GetTicks() - m_ticks) / 1000 / 60;
769 			if (remaining < 0) remaining = 0;
770 			return remaining;
771 		}
772 
773 		std::string format_for_chat(const std::string& player_name) const;
774 		std::string game_string() const;
775 
776 		// Conformance to w_items_in_game<>'s Element interface
nameGameListEntry777 		const std::string& name() const { return m_description.m_name; }
778 
779 		uint32		m_gameID;
780 		uint8		m_ipAddress[4];
781 		uint16		m_port;
782 		uint8		m_verb;
783 		uint8		m_gameEnable;
784 		uint32		m_timeRemaining;
785 		uint32		m_hostPlayerID;
786 		uint16		m_len;
787 		GameDescription	m_description;
788 
789 		uint32          m_ticks; // SDL ticks at last update
790 		std::string     m_hostPlayerName;
791 
GameListEntryGameListEntry792 	GameListEntry() : m_target(false) { }
793 		bool            m_target;
794 	};
795 
796 	enum { kType = kSERVER_GAMELIST };
797 
type()798 	MessageTypeID type() const { return kType; }
799 
clone()800 	GameListMessage* clone() const
801 	{ return new GameListMessage(*this); }
802 
entries()803 	const std::vector<GameListEntry>& entries() const { return m_entries; }
804 
805 protected:
reallyDeflateTo(AOStream & thePacket)806 	void reallyDeflateTo(AOStream& thePacket) const
807 	{
808 		// no need for deflation
809 		assert(false);
810 	}
811 
812 	bool reallyInflateFrom(AIStream& inStream);
813 
814 private:
815 	std::vector<GameListEntry>	m_entries;
816 };
817 
818 AIStream& operator >>(AIStream& stream, GameListMessage::GameListEntry& entry);
819 std::ostream& operator <<(std::ostream& stream, const GameListMessage::GameListEntry& entry);
820 
821 
822 
823 typedef DatalessMessage<kCLIENT_RESETGAME>	ResetGameMessage;
824 typedef DatalessMessage<kCLIENT_REMOVEGAME>	RemoveGameMessage;
825 
826 
827 
828 class StartGameMessage : public SmallMessageHelper
829 {
830 public:
831 	enum { kType = kCLIENT_STARTGAME };
832 
type()833 	MessageTypeID type() const { return kType; }
834 
clone()835 	StartGameMessage* clone() const
836 	{ return new StartGameMessage(*this); }
837 
StartGameMessage(int32 gameTimeInSeconds)838 	StartGameMessage(int32 gameTimeInSeconds) : m_gameTimeInSeconds(gameTimeInSeconds) {}
839 
840 protected:
841 	void reallyDeflateTo(AOStream& thePacket) const;
842 
reallyInflateFrom(AIStream & inStream)843 	bool reallyInflateFrom(AIStream& inStream)
844 	{
845 		// no need for inflation
846 		assert(false);
847 		return false;
848 	}
849 
850 private:
851 	int32	m_gameTimeInSeconds;
852 };
853 
854 #endif // METASERVER_MESSAGES_H
855