1 /*
2  *  network_metaserver.h - 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 NETWORK_METASERVER_H
26 #define NETWORK_METASERVER_H
27 
28 #include "metaserver_messages.h" // RoomDescription
29 
30 #include <exception>
31 #include <vector>
32 #include <map>
33 #include <memory> // unique_ptr
34 #include <set>
35 #include <stdexcept>
36 
37 #include "Logging.h"
38 
39 template <typename tElement>
40 class MetaserverMaintainedList
41 {
42 public:
MetaserverMaintainedList()43 	MetaserverMaintainedList() : m_target(Element::IdNone) { }
44 	typedef tElement		 	Element;
45 	typedef typename Element::IDType	IDType;
46 
clear()47 	void clear() { m_entries.clear(); }
48 
processUpdates(const std::vector<Element> & updates)49 	void processUpdates(const std::vector<Element>& updates)
50 	{
51 		for(size_t i = 0; i < updates.size(); i++)
52 			processUpdate(updates[i].verb(), updates[i].id(), updates[i]);
53 
54 		if (m_target != Element::IdNone)
55 		{
56 			typename Map::iterator target = m_entries.find(m_target);
57 			if (target != m_entries.end())
58 			{
59 				target->second.target(true);
60 			}
61 			else
62 			{
63 				m_target = Element::IdNone;
64 			}
65 		}
66 	}
67 
entries()68 	const std::vector<Element> entries() const
69 	{
70 		std::vector<Element>	result;
71 
72 		for(typename Map::const_iterator i = m_entries.begin(); i != m_entries.end(); ++i)
73 		{
74 			result.push_back(i->second);
75 		}
76 
77 		return result;
78 	}
79 
find(IDType id)80 	const Element* find(IDType id) const
81 	{
82 		typename Map::const_iterator it = m_entries.find(id);
83 		if (it != m_entries.end())
84 		{
85 			return &it->second;
86 		}
87 		else
88 		{
89 			return 0;
90 		}
91 	}
92 
93 	enum
94 	{
95 		kAdd		= 0,
96 		kDelete		= 1,
97 		kRefresh	= 2
98 	};
99 
target()100 	IDType target() { return m_target; }
target(IDType id)101 	void target(IDType id)
102 	{
103 		// clear the old target
104 		typename Map::iterator old_target = (m_target == Element::IdNone) ? m_entries.end() : m_entries.find(m_target);
105 		if (old_target != m_entries.end())
106 		{
107 			old_target->second.target(false);
108 		}
109 
110 		typename Map::iterator e = (id == Element::IdNone) ? m_entries.end() : m_entries.find(id);
111 		if (e == m_entries.end())
112 		{
113 			m_target = Element::IdNone;
114 		}
115 		else
116 		{
117 			m_target = id;
118 			e->second.target(true);
119 		}
120 	}
121 
122 
123 private:
124 	typedef std::map<IDType, Element>	Map;
125 
126 	Map	m_entries;
127 
processUpdate(uint8 verb,IDType id,const Element & update)128 	void processUpdate(uint8 verb, IDType id, const Element& update)
129 	{
130 		switch(verb)
131 		{
132 			case kAdd:
133 				if(m_entries.find(id) != m_entries.end())
134 				{
135 					logAnomaly("received instruction to add item with same ID (%d) as known item; using the new one only", id);
136 					m_entries.erase(id);
137 				}
138 				m_entries.insert(typename Map::value_type(id, update));
139 				break;
140 
141 			case kDelete:
142 				if(m_entries.erase(id) == 0)
143 				{
144 					logAnomaly("received instruction to delete unknown item (ID %d)", id);
145 				}
146 				break;
147 
148 			case kRefresh:
149 				if(m_entries.erase(id) == 0)
150 				{
151 					logAnomaly("received instruction to refresh unknown item (ID %d); treating it as an add", id);
152 				}
153 				m_entries.insert(typename Map::value_type(id, update));
154 				break;
155 
156 			default:
157 				logAnomaly("unknown list item verb %d - ignored", verb);
158 				break;
159 		}
160 	}
161 
162 	IDType m_target;
163 };
164 
165 
166 
167 class CommunicationsChannel;
168 class MessageInflater;
169 class MessageHandler;
170 class MessageDispatcher;
171 
172 class Message;
173 class ChatMessage;
174 class BroadcastMessage;
175 
176 class MetaserverClient
177 {
178 public:
179         class NotificationAdapter
180         {
181         public:
182                 virtual void playersInRoomChanged(const std::vector<MetaserverPlayerInfo>&) = 0;
183                 virtual void gamesInRoomChanged(const std::vector<GameListMessage::GameListEntry>&) = 0;
184                 virtual void receivedChatMessage(const std::string& senderName, uint32 senderID, const std::string& message) = 0;
185 		virtual void receivedLocalMessage(const std::string& message) = 0;
186                 virtual void receivedBroadcastMessage(const std::string& message) = 0;
187                 virtual void receivedPrivateMessage(const std::string& senderName, uint32 senderID, const std::string& message) = 0;
188 		virtual void roomDisconnected() = 0;
~NotificationAdapter()189                 virtual ~NotificationAdapter() {}
190         };
191 
192 		class NotificationAdapterInstaller
193 		{
194 		public:
NotificationAdapterInstaller(NotificationAdapter * adapter,MetaserverClient & metaserverClient)195 			NotificationAdapterInstaller(NotificationAdapter* adapter, MetaserverClient& metaserverClient)
196 				: m_adapter(adapter), m_metaserverClient(metaserverClient)
197 			{
198 				m_previousAdapter = m_metaserverClient.notificationAdapter();
199 				m_metaserverClient.associateNotificationAdapter(m_adapter);
200 			}
201 
~NotificationAdapterInstaller()202 			~NotificationAdapterInstaller()
203 			{
204 				assert(m_metaserverClient.notificationAdapter() == m_adapter);
205 				m_metaserverClient.associateNotificationAdapter(m_previousAdapter);
206 			}
207 
208 		private:
209 			NotificationAdapter*	m_previousAdapter;
210 			NotificationAdapter*	m_adapter;
211 			MetaserverClient&		m_metaserverClient;
212 
213 			NotificationAdapterInstaller(const NotificationAdapterInstaller&);
214 			NotificationAdapterInstaller& operator =(const NotificationAdapterInstaller&);
215 		};
216 
217         typedef std::vector<RoomDescription>					Rooms;
218 	typedef MetaserverMaintainedList<MetaserverPlayerInfo>			PlayersInRoom;
219 	typedef MetaserverMaintainedList<GameListMessage::GameListEntry>	GamesInRoom;
220 
221 
222 	MetaserverClient();
223 
associateNotificationAdapter(NotificationAdapter * adapter)224         void associateNotificationAdapter(NotificationAdapter* adapter)
225                 { m_notificationAdapter = adapter; }
notificationAdapter()226         NotificationAdapter* notificationAdapter() const { return m_notificationAdapter; }
227 
228 	class LoginDeniedException : public std::runtime_error
229 	{
230 	public:
231 		enum {
232 			SyntaxError,
233 			GamesNotAllowed,
234 			InvalidVersion,
235 			BadUserOrPassword,
236 			UserNotLoggedIn,
237 			BadMetaserverVersion,
238 			UserAlreadyLoggedIn,
239 			UnknownGameType,
240 			LoginSuccessful,
241 			LogoutSuccessful,
242 			PlayerNotInRoom,
243 			GameAlreadyExists,
244 			AccountAlreadyLoggedIn,
245 			RoomFull,
246 			AccountLocked,
247 			NotSupported
248 		};
249 
LoginDeniedException(int code,const std::string & arg)250 		LoginDeniedException(int code, const std::string& arg) : std::runtime_error(arg), m_code(code) { }
code()251 		int code() const { return m_code; }
252 	private:
253 		int m_code;
254 	};
255 	class ServerConnectException : public std::runtime_error
256 	{
257 	public:
ServerConnectException(const std::string & arg)258 		ServerConnectException(const std::string& arg) : std::runtime_error(arg) { }
259 	};
260 
261         void connect(const std::string& serverName, uint16 port, const std::string& userName, const std::string& userPassword);
262 	void disconnect();
263 	bool isConnected() const;
264 
265 	void setPlayerName(const std::string& name);
playerName()266 	const std::string& playerName() const { return m_playerName; }
267 
268 	void setAway(bool away, const std::string& away_message);
269 	void setMode(uint16 mode, const std::string& session_id);
270 
271 	void setPlayerTeamName(const std::string& team);
272 
273 	const Rooms& rooms() const;
274 	void setRoom(const RoomDescription& room);
275 
playersInRoom()276         const std::vector<MetaserverPlayerInfo> playersInRoom() const { return m_playersInRoom.entries(); }
gamesInRoom()277         const std::vector<GameListMessage::GameListEntry> gamesInRoom() const { return m_gamesInRoom.entries(); }
278 
279 	void pump();
280 	static void pumpAll();
281 
282 	void sendChatMessage(const std::string& message);
283 	void sendPrivateMessage(MetaserverPlayerInfo::IDType destination, const std::string& message);
284 	void announceGame(uint16 gamePort, const GameDescription& description);
285 	void announcePlayersInGame(uint8 players);
286 	void announceGameStarted(int32 gameTimeInSeconds);
287 	void announceGameReset();
288 	void announceGameDeleted();
289 	void ignore(const std::string& name);
290 	void ignore(MetaserverPlayerInfo::IDType id);
291 	bool is_ignored(MetaserverPlayerInfo::IDType id);
292 	void syncGames();
293 
player_target(MetaserverPlayerInfo::IDType id)294 	void player_target(MetaserverPlayerInfo::IDType id) { m_playersInRoom.target(id); };
player_target()295 	MetaserverPlayerInfo::IDType player_target() { return m_playersInRoom.target(); };
find_player(MetaserverPlayerInfo::IDType id)296 	const MetaserverPlayerInfo* find_player(MetaserverPlayerInfo::IDType id) { return m_playersInRoom.find(id); }
game_target(GameListMessage::GameListEntry::IDType id)297 	void game_target(GameListMessage::GameListEntry::IDType id) { m_gamesInRoom.target(id); }
game_target()298 	GameListMessage::GameListEntry::IDType game_target() { return m_gamesInRoom.target(); };
find_game(GameListMessage::GameListEntry::IDType id)299 	const GameListMessage::GameListEntry* find_game(GameListMessage::GameListEntry::IDType id) { return m_gamesInRoom.find(id); }
300 
301 	~MetaserverClient();
302 
303 private:
304 	void handleUnexpectedMessage(Message* inMessage, CommunicationsChannel* inChannel);
305 	void handleChatMessage(ChatMessage* inMessage, CommunicationsChannel* inChannel);
306 	void handlePrivateMessage(PrivateMessage* inMessage, CommunicationsChannel* inChannel);
307 	void handleKeepAliveMessage(Message* inMessage, CommunicationsChannel* inChannel);
308 	void handleBroadcastMessage(BroadcastMessage* inMessage, CommunicationsChannel* inChannel);
309         void handlePlayerListMessage(PlayerListMessage* inMessage, CommunicationsChannel* inChannel);
310         void handleRoomListMessage(RoomListMessage* inMessage, CommunicationsChannel* inChannel);
311         void handleGameListMessage(GameListMessage* inMessage, CommunicationsChannel* inChannel);
handleSetPlayerDataMessage(SetPlayerDataMessage *,CommunicationsChannel *)312 	void handleSetPlayerDataMessage(SetPlayerDataMessage*, CommunicationsChannel *) { }
313 
314 	std::unique_ptr<CommunicationsChannel>    m_channel;
315 	std::unique_ptr<MessageInflater>          m_inflater;
316 	std::unique_ptr<MessageDispatcher>        m_dispatcher;
317 	std::unique_ptr<MessageDispatcher>        m_loginDispatcher;
318 	std::unique_ptr<MessageHandler>           m_unexpectedMessageHandler;
319 	std::unique_ptr<MessageHandler>           m_chatMessageHandler;
320 	std::unique_ptr<MessageHandler>           m_keepAliveMessageHandler;
321 	std::unique_ptr<MessageHandler>           m_broadcastMessageHandler;
322 	std::unique_ptr<MessageHandler>           m_playerListMessageHandler;
323 	std::unique_ptr<MessageHandler>           m_roomListMessageHandler;
324 	std::unique_ptr<MessageHandler>           m_gameListMessageHandler;
325 	std::unique_ptr<MessageHandler>           m_privateMessageHandler;
326 	std::unique_ptr<MessageHandler>           m_setPlayerDataMessageHandler;
327 	Rooms					m_rooms;
328 	RoomDescription				m_room;
329         PlayersInRoom				m_playersInRoom;
330         GamesInRoom				m_gamesInRoom;
331 	std::string				m_playerName;
332 	std::string				m_teamName;
333         NotificationAdapter*			m_notificationAdapter;
334 	uint32					m_playerID;
335 
336 	static std::set<MetaserverClient*>	s_instances;
337 	static std::set<std::string>            s_ignoreNames;
338 
339 	GameDescription                         m_gameDescription;
340 	uint16                                  m_gamePort;
341 
342 	MetaserverPlayerInfo::IDType            m_player_target;
343 	bool                                    m_player_target_exists;
344 
345 	bool                                    m_notifiedOfDisconnected;
346 	bool                                    m_gameAnnounced;
347 };
348 
349 #endif // NETWORK_METASERVER_H
350