1 #ifndef __LOBBY_2_SERVER_H
2 #define __LOBBY_2_SERVER_H
3 
4 #include "Export.h"
5 #include "RakNetTypes.h"
6 #include "Lobby2Plugin.h"
7 #include "DS_OrderedList.h"
8 #include "ThreadPool.h"
9 #include "Lobby2Presence.h"
10 
11 //class PostgreSQLInterface;
12 
13 namespace RakNet
14 {
15 
16 struct Lobby2Message;
17 class RoomsPlugin;
18 
19 /// Commands are either messages from remote systems, or can be run by the local system
20 /// \internal
21 struct Lobby2ServerCommand
22 {
23 	Lobby2Message *lobby2Message;
24 	bool deallocMsgWhenDone;
25 	bool returnToSender;
26 	unsigned int callerUserId;
27 	RakNet::RakString callingUserName;
28 	DataStructures::List<SystemAddress> callerSystemAddresses;
29 	DataStructures::List<RakNetGUID> callerGuids;
30 	//SystemAddress requiredConnectionAddress;
31 	Lobby2Server *server;
32 };
33 
34 /// \brief The base class for the lobby server, without database specific functionality
35 /// \details This is a plugin which will take incoming messages via Lobby2Client_PC::SendMessage(), process them, and send back the same messages with output and a result code
36 /// Unlike the first implementation of the lobby server, this is a thin plugin that mostly just sends messages to threads and sends back the results.
37 /// \ingroup LOBBY_2_SERVER
38 class RAK_DLL_EXPORT Lobby2Server : public RakNet::Lobby2Plugin, public ThreadDataInterface
39 {
40 public:
41 	Lobby2Server();
42 	virtual ~Lobby2Server();
43 
44 	/// \brief Connect to the database \a numWorkerThreads times using the connection string
45 	/// \param[in] conninfo See the postgre docs
46 	/// \return True on success, false on failure.
47 	virtual bool ConnectToDB(const char *conninfo, int numWorkerThreads)=0;
48 	/// \internal
49 	virtual void AddInputFromThread(Lobby2Message *msg, unsigned int targetUserId, RakNet::RakString targetUserHandle)=0;
50 	/// \internal
51 	virtual void AddOutputFromThread(Lobby2Message *msg, unsigned int targetUserId, RakNet::RakString targetUserHandle)=0;
52 
53 	/// \brief Lobby2Message encapsulates a user command, containing both input and output data
54 	/// \details This will serialize and transmit that command
55 	void SendMessage(Lobby2Message *msg, const DataStructures::List<SystemAddress> &recipients);
56 
57 	/// \brief Add a command, which contains a message and other data such as who send the message.
58 	/// \details The command will be processed according to its implemented virtual functions. Most likely it will be processed in a thread to run database commands
59 	void ExecuteCommand(Lobby2ServerCommand *command);
60 
61 	/// \brief If Lobby2Message::RequiresAdmin() returns true, the message can only be processed from a remote system if the sender's system address is first added()
62 	/// \details This is useful if you want to administrate the server remotely
63 	void AddAdminAddress(SystemAddress addr);
64 	/// \brief If AddAdminAddress() was previously called with \a addr then this returns true.
65 	bool HasAdminAddress(const DataStructures::List<SystemAddress> &addresses);
66 	/// \brief Removes a system address previously added with AddAdminAddress()
67 	void RemoveAdminAddress(SystemAddress addr);
68 	/// \brief Removes all system addresses previously added with AddAdminAddress()
69 	void ClearAdminAddresses(void);
70 
71 	/// \brief If Lobby2Message::RequiresRankingPermission() returns true, then the system that sent the command must be registered with AddRankingAddress()
72 	/// \param[in] addr Address to allow
73 	void AddRankingAddress(SystemAddress addr);
74 
75 	/// Returns if an address was previously added with AddRankingAddress()
76 	/// \param[in] addr Address to check
77 	bool HasRankingAddress(const DataStructures::List<SystemAddress> &addresses);
78 
79 	/// Removes an addressed added with AddRankingAddress()
80 	/// \param[in] addr Address to check
81 	void RemoveRankingAddress(SystemAddress addr);
82 
83 	/// \brief Clears all addresses added with AddRankingAddress()
84 	void ClearRankingAddresses(void);
85 
86 	/// \brief To use RoomsPlugin and Lobby2Server together, register RoomsPlugin with this funcrtion
87 	/// \details The rooms plugin does not automatically handle users logging in and logging off, or renaming users.
88 	/// You can have Lobby2Server manage this for you by calling SetRoomsPlugin() with a pointer to the rooms plugin, if it is on the local system.
89 	/// This will call RoomsPlugin::LoginRoomsParticipant() and RoomsPlugin::LogoffRoomsParticipant() as the messages L2MID_Client_Login and L2MID_Client_Logoff arrive
90 	/// This will use the pointer to RoomsPlugin directly. Setting this will disable SetRoomsPluginAddress()
91 	/// \note This is an empty function. You must #define __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN and link with RoomsPlugin.h to use it()
92 	void SetRoomsPlugin(RoomsPlugin *rp);
93 
94 	/// \brief This is similar to SetRoomsPlugin(), except the plugin is on another system.
95 	/// \details This will set the system address of that system to send the login and logoff commands to.
96 	/// For this function to work, you must first call RoomsPlugin::AddLoginServerAddress() so that RoomsPlugin will accept the incoming login and logoff messages.
97 	/// \note This is an empty function. You must #define __INTEGRATE_LOBBY2_WITH_ROOMS_PLUGIN and link with RoomsPlugin.h to use it()
98 	void SetRoomsPluginAddress(SystemAddress address);
99 
100 	/// \brief Server configuration properties, to customize how the server runs specific commands
101 	struct ConfigurationProperties
102 	{
ConfigurationPropertiesConfigurationProperties103 		ConfigurationProperties() {requiresEmailAddressValidationToLogin=false; requiresTitleToLogin=false; accountRegistrationRequiredAgeYears=0;}
104 
105 		/// \brief If true, Client_Login will fail with Client_Login_EMAIL_ADDRESS_NOT_VALIDATED unless the email address registered with Client_RegisterAccount is verified with the command System_SetEmailAddressValidated
106 		bool requiresEmailAddressValidationToLogin;
107 
108 		/// \brief If true Client_Login::titleName and Client_Login::titleSecretKey must be previously registered with System_CreateTitle or Client_Login will fail with L2RC_Client_Login_BAD_TITLE_OR_TITLE_SECRET_KEY
109 		bool requiresTitleToLogin;
110 
111 		/// \brief If true, Client_RegisterAccount::cdKey must be previously registered with CDKey_Add or Client_RegisterAccount will fail with L2RC_Client_RegisterAccount_REQUIRES_CD_KEY or a related error message
112 		bool accountRegistrationRequiresCDKey;
113 
114 		/// \brief The minimum age needed to register accounts. If Client_RegisterAccount::createAccountParameters::ageInDays is less than this, then the account registration will fail with L2RC_Client_RegisterAccount_REQUIRED_AGE_NOT_MET
115 		/// \details Per-title age requirements can be handled client-side with System_CreateTitle::requiredAge and System_GetTitleRequiredAge
116 		unsigned int accountRegistrationRequiredAgeYears;
117 	};
118 
119 	/// \brief Set the desired configuration properties. This is read during runtime from threads.
120 	void SetConfigurationProperties(ConfigurationProperties c);
121 	/// \brief Get the previously set configuration properties.
122 	const ConfigurationProperties *GetConfigurationProperties(void) const;
123 
124 	/// Set the presence of a logged in user
125 	/// \param[in] presence Presence info of this user
126 	void SetPresence(const RakNet::Lobby2Presence &presence, RakNet::RakString userHandle);
127 
128 	/// Get the presence of a logged in user, by handle
129 	/// \param[out] presence Presence info of requested user
130 	/// \param[in] userHandle Handle of the user
131 	void GetPresence(RakNet::Lobby2Presence &presence, RakNet::RakString userHandle);
132 
133 	/// \internal Lets the plugin know that a user has logged on, so this user can be tracked and the message forwarded to RoomsPlugin
134 	void OnLogin(Lobby2ServerCommand *command, bool calledFromThread);
135 	/// \internal Lets the plugin know that a user has logged off, so this user can be tracked and the message forwarded to RoomsPlugin
136 	void OnLogoff(Lobby2ServerCommand *command, bool calledFromThread);
137 	/// \internal Lets the plugin know that a user has been renamed, so this user can be tracked and the message forwarded to RoomsPlugin
138 	void OnChangeHandle(Lobby2ServerCommand *command, bool calledFromThread);
139 
140 	/// \internal
141 	struct User
142 	{
143 		DataStructures::List<SystemAddress> systemAddresses;
144 		DataStructures::List<RakNetGUID> guids;
145 		unsigned int callerUserId;
146 		RakNet::RakString userName;
147 		Lobby2Presence presence;
148 		bool allowMultipleLogins;
149 	};
150 
151 	/// \internal
152 	static int UserCompByUsername( const RakString &key, Lobby2Server::User * const &data );
153 
154 	/// \internal
155 	struct ThreadAction
156 	{
157 		Lobby2MessageID action;
158 		Lobby2ServerCommand command;
159 	};
160 
GetUsers(void)161 	const DataStructures::OrderedList<RakString, User*, Lobby2Server::UserCompByUsername>& GetUsers(void) const {return users;}
162 	void GetUserOnlineStatus(UsernameAndOnlineStatus &userInfo) const;
163 
164 
165 protected:
166 
167 	void Update(void);
168 	PluginReceiveResult OnReceive(Packet *packet);
169 	void OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
170 	void OnShutdown(void);
171 	void OnMessage(Packet *packet);
172 	void Clear(void);
173 	void ClearUsers(void);
174 	unsigned int GetUserIndexBySystemAddress(SystemAddress systemAddress) const;
175 	unsigned int GetUserIndexByGUID(RakNetGUID guid) const;
176 	unsigned int GetUserIndexByUsername(RakNet::RakString userName) const;
177 	void StopThreads(void);
178 	void SendRemoteLoginNotification(RakNet::RakString handle, const DataStructures::List<SystemAddress>& recipients);
179 
180 	/// \internal
181 	void RemoveUser(RakString userName);
182 	/// \internal
183 	void RemoveUser(unsigned int index);
184 	void LogoffFromRooms(User *user);
185 
186 	virtual void* PerThreadFactory(void *context)=0;
187 	virtual void PerThreadDestructor(void* factoryResult, void *context)=0;
188 	virtual void AddInputCommand(Lobby2ServerCommand command)=0;
ClearConnections(void)189 	virtual void ClearConnections(void) {};
190 
191 	DataStructures::OrderedList<SystemAddress, SystemAddress> adminAddresses;
192 	DataStructures::OrderedList<SystemAddress, SystemAddress> rankingAddresses;
193 	DataStructures::OrderedList<RakString, User*, Lobby2Server::UserCompByUsername> users;
194 	RoomsPlugin *roomsPlugin;
195 	SystemAddress roomsPluginAddress;
196 	ThreadPool<Lobby2ServerCommand,Lobby2ServerCommand> threadPool;
197 	SimpleMutex connectionPoolMutex;
198 	ConfigurationProperties configurationProperties;
199 	DataStructures::Queue<ThreadAction> threadActionQueue;
200 	SimpleMutex threadActionQueueMutex;
201 
202 	//DataStructures::List<PostgreSQLInterface *> connectionPool;
203 	void SendUnifiedToMultiple( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, const DataStructures::List<SystemAddress> systemAddresses );
204 };
205 
206 }
207 
208 #endif