1 #ifndef __LOBBY_ROOM_H
2 #define __LOBBY_ROOM_H
3 
4 #include "DS_Map.h"
5 #include "DS_Table.h"
6 #include "RoomsErrorCodes.h"
7 #include "DS_List.h"
8 #include "RakNetTypes.h"
9 #include "IntervalTimer.h"
10 #include "RoomTypes.h"
11 
12 class ProfanityFilter;
13 
14 namespace RakNet
15 {
16 
17 class Room;
18 class PerGameRoomList;
19 class PerGameRoomsContainer;
20 class BitStream;
21 typedef unsigned int RoomID;
22 struct QuickJoinUser;
23 struct RoomMember;
24 class AllGamesRoomsContainer;
25 
26 class RoomsParticipant
27 {
28 public:
RoomsParticipant()29 	RoomsParticipant() {room=0; inQuickJoin=false;}
~RoomsParticipant()30 	~RoomsParticipant() {}
GetRoom(void)31 	Room * GetRoom(void) const {return room;}
SetPerGameRoomsContainer(PerGameRoomsContainer * p)32 	void SetPerGameRoomsContainer(PerGameRoomsContainer *p) {perGameRoomsContainer=p;}
SetRoom(Room * _room)33 	void SetRoom(Room *_room) {room=_room; inQuickJoin=false;}
SetInQuickJoin(bool b)34 	void SetInQuickJoin(bool b) {inQuickJoin=b; if (b) room=0;}
35 
36 	// Name is used for persistent invites and bans. Name should be unique among all participants or else the invites and bans will be applied to the wrong players
GetName(void)37 	RakNet::RakString GetName(void) const {return name;}
SetName(const char * str)38 	void SetName(const char *str) {name = str;}
SetSystemAddress(SystemAddress sa)39 	void SetSystemAddress(SystemAddress sa) {systemAddress=sa;}
GetSystemAddress(void)40 	SystemAddress GetSystemAddress(void) const {return systemAddress;}
SetGUID(RakNetGUID g)41 	void SetGUID(RakNetGUID g) {guid=g;}
GetGUID(void)42 	RakNetGUID GetGUID(void) const {return guid;}
43 
GetPerGameRoomsContainer(void)44 	PerGameRoomsContainer *GetPerGameRoomsContainer(void) const {return perGameRoomsContainer;}
GetInQuickJoin(void)45 	bool GetInQuickJoin(void) const {return inQuickJoin;}
46 protected:
47 	RakNet::RakString name;
48 	SystemAddress systemAddress;
49 	RakNetGUID guid;
50 	Room *room;
51 	bool inQuickJoin;
52 	PerGameRoomsContainer *perGameRoomsContainer;
53 };
54 
55 typedef RakNet::RakString GameIdentifier;
56 
57 enum RoomLockState
58 {
59 	// Anyone can join or leave
60 	RLS_NOT_LOCKED,
61 	// Anyone can join as spectator or become spectator. New players are not allowed. You cannot leave spectator.
62 	RLS_PLAYERS_LOCKED,
63 	// No new players are allowed, and you cannot toggle spectator
64 	RLS_ALL_LOCKED
65 };
66 
67 enum ParticipantCanJoinRoomResult
68 {
69 	PCJRR_SUCCESS,
70 	PCJRR_BANNED,
71 	PCJRR_NO_PUBLIC_SLOTS,
72 	PCJRR_NO_PUBLIC_OR_RESERVED_SLOTS,
73 	PCJRR_NO_SPECTATOR_SLOTS,
74 	PCJRR_LOCKED,
75 	PCJRR_SLOT_ALREADY_USED,
76 };
77 
78 struct Slots
79 {
80 	Slots();
81 	~Slots();
82 	unsigned int publicSlots;
83 	unsigned int reservedSlots;
84 	unsigned int spectatorSlots;
GetTotalSlotsSlots85 	unsigned int GetTotalSlots(void) const {return publicSlots+reservedSlots+spectatorSlots;}
86 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
87 	RoomsErrorCode Validate(void) const;
88 };
89 
90 struct InvitedUser
91 {
InvitedUserInvitedUser92 	InvitedUser() {room=0; roomId=0; invitedAsSpectator=false;}
93 	Room *room;
94 	RoomID roomId;
95 	RakNet::RakString invitorName;
96 	SystemAddress invitorSystemAddress;
97 	RakNet::RakString target;
98 	RakNet::RakString subject;
99 	RakNet::RakString body;
100 	bool invitedAsSpectator;
101 
102 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
103 };
104 
105 struct BannedUser
106 {
107 	RakNet::RakString target;
108 	RakNet::RakString reason;
109 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
110 };
111 
112 struct RemoveUserResult
113 {
114 	RemoveUserResult();
115 	~RemoveUserResult();
116 
117 	// Why return a deleted pointer?
118 //	RoomsParticipant *removedUser;
119 	bool removedFromQuickJoin;
120 	bool removedFromRoom;
121 	SystemAddress removedUserAddress;
122 	RakNet::RakString removedUserName;
123 
124 	// Following members only apply if removedFromRoom==true
125 	Room *room;
126 	RoomID roomId;
127 	bool gotNewModerator; // If you were the moderator before, this is true
128 	DataStructures::List<InvitedUser> clearedInvitations; // If invitations were cleared when you leave, these are the invitations
129 	bool roomDestroyed; // Up to caller to deallocate
130 
131 	QuickJoinUser *qju;
132 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
133 };
134 
135 
136 struct RoomMemberDescriptor
137 {
138 	RakNet::RakString name;
139 	RoomMemberMode roomMemberMode;
140 	bool isReady;
141 	// Filled externally
142 	SystemAddress systemAddress;
143 	RakNetGUID guid;
144 
145 	void FromRoomMember(RoomMember *roomMember);
146 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
147 };
148 
149 struct NetworkedRoomCreationParameters
150 {
NetworkedRoomCreationParametersNetworkedRoomCreationParameters151 	NetworkedRoomCreationParameters() {hiddenFromSearches=false; destroyOnModeratorLeave=false; autoLockReadyStatus=false; inviteToRoomPermission=INVITE_MODE_ANYONE_CAN_INVITE; inviteToSpectatorSlotPermission=INVITE_MODE_ANYONE_CAN_INVITE; clearInvitesOnNewModerator=false;}
152 	// Checked by Validate
153 	Slots slots;
154 	bool hiddenFromSearches;
155 	bool destroyOnModeratorLeave;
156 	bool autoLockReadyStatus; // When everyone is ready and (the room is full or the room is locked), don't allow users to set unready.
157 	enum SendInvitePermission
158 	{
159 		INVITE_MODE_ANYONE_CAN_INVITE,
160 		INVITE_MODE_MODERATOR_CAN_INVITE,
161 		INVITE_MODE_PUBLIC_SLOTS_CAN_INVITE,
162 		INVITE_MODE_RESERVED_SLOTS_CAN_INVITE,
163 		INVITE_MODE_SPECTATOR_SLOTS_CAN_INVITE,
164 		INVITE_MODE_MODERATOR_OR_PUBLIC_SLOTS_CAN_INVITE,
165 		INVITE_MODE_MODERATOR_OR_PUBLIC_OR_RESERVED_SLOTS_CAN_INVITE,
166 	} inviteToRoomPermission, inviteToSpectatorSlotPermission;
167 	bool clearInvitesOnNewModerator; // Leave or change
168 	RakNet::RakString roomName;
169 
170 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
171 	static const char *SendInvitePermissionToEnum(SendInvitePermission e);
172 };
173 struct RoomDescriptor
174 {
175 	DataStructures::List<RoomMemberDescriptor> roomMemberList;
176 	DataStructures::List<BannedUser> banList;
177 	RoomLockState roomLockState;
178 	RoomID lobbyRoomId;
179 	bool autoLockReadyStatus;
180 	bool hiddenFromSearches;
181 	NetworkedRoomCreationParameters::SendInvitePermission inviteToRoomPermission;
182 	NetworkedRoomCreationParameters::SendInvitePermission inviteToSpectatorSlotPermission;
183 	DataStructures::Table roomProperties;
184 
GetPropertyRoomDescriptor185 	DataStructures::Table::Cell *GetProperty(const char* columnName)
186 	{
187 		return roomProperties.GetRowByIndex(0,0)->cells[roomProperties.ColumnIndex(columnName)];
188 	}
GetPropertyRoomDescriptor189 	DataStructures::Table::Cell *GetProperty(int index)
190 	{
191 		return roomProperties.GetRowByIndex(0,0)->cells[index];
192 	}
193 
ClearRoomDescriptor194 	void Clear(void)
195 	{
196 		roomMemberList.Clear(false, __FILE__, __LINE__);
197 		banList.Clear(false, __FILE__, __LINE__);
198 		roomProperties.Clear();
199 	}
200 	void FromRoom(Room *room, AllGamesRoomsContainer *agrc);
201 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
202 };
203 
204 struct JoinedRoomResult
205 {
JoinedRoomResultJoinedRoomResult206 	JoinedRoomResult() {roomOutput=0; acceptedInvitor=0; agrc=0; joiningMember=0;}
~JoinedRoomResultJoinedRoomResult207 	~JoinedRoomResult() {}
208 	Room* roomOutput;
209 	RoomDescriptor roomDescriptor;
210 	RoomsParticipant* acceptedInvitor;
211 	RakNet::RakString acceptedInvitorName;
212 	SystemAddress acceptedInvitorAddress;
213 	RoomsParticipant* joiningMember;
214 	RakNet::RakString joiningMemberName;
215 	SystemAddress joiningMemberAddress;
216 
217 	// Needed to serialize
218 	AllGamesRoomsContainer *agrc;
219 
220 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream );
221 };
222 
223 
224 struct RoomCreationParameters
225 {
226 	RoomCreationParameters();
227 	~RoomCreationParameters();
228 
229 	NetworkedRoomCreationParameters networkedRoomCreationParameters;
230 
231 	// Not checked
232 	RoomsParticipant* firstUser;
233 	GameIdentifier gameIdentifier;
234 
235 	// Output parameters:
236 	// Was the room created?
237 	bool createdRoom;
238 	Room *roomOutput;
239 
240 	// May return REC_ROOM_CREATION_PARAMETERS_* or REC_SUCCESS
241 	RoomsErrorCode Validate(
242 		const DataStructures::List<RakNet::RakString> &otherRoomNames,
243 		ProfanityFilter *profanityFilter) const;
244 };
245 
246 struct RoomMember
247 {
248 	RoomMember();
249 	~RoomMember();
250 	RoomsParticipant* roomsParticipant;
251 	RoomMemberMode roomMemberMode;
252 	RakNetTime joinTime;
253 	bool isReady;
254 	// Internal - set to false when a new member is added. When the other members have been told about this member, it is set to true
255 	bool newMemberNotificationProcessed;
256 };
257 struct KickedUser
258 {
259 	RoomsParticipant* roomsParticipant;
260 	RakNet::RakString reason;
261 };
262 
263 struct RoomQuery
264 {
265 	RoomQuery();
266 	~RoomQuery();
267 
268 	// Point to an externally allocated array of FilterQuery, or use the helper functions below to use a static array (not threadsafe to use the static array)
269 	DataStructures::Table::FilterQuery *queries;
270 	// Size of the queries array
271 	unsigned int numQueries;
272 	// Not used
273 	bool queriesAllocated;
274 
275 	// Helper functions
276 	// Easier to use, but not threadsafe
277 	void AddQuery_NUMERIC(const char *columnName, double numericValue, DataStructures::Table::FilterQueryType op=DataStructures::Table::QF_EQUAL);
278 	void AddQuery_STRING(const char *columnName, const char *charValue, DataStructures::Table::FilterQueryType op=DataStructures::Table::QF_EQUAL);
279 	void AddQuery_BINARY(const char *columnName, const char *input, int inputLength, DataStructures::Table::FilterQueryType op=DataStructures::Table::QF_EQUAL);
280 	void AddQuery_POINTER(const char *columnName, void *ptr, DataStructures::Table::FilterQueryType op=DataStructures::Table::QF_EQUAL);
281 	RoomsErrorCode Validate(void);
282 
283 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
284 
285 	/// \internal
286 	void SetQueriesToStatic(void);
287 
288 private:
289 	static DataStructures::Table::FilterQuery fq[32];
290 	static DataStructures::Table::Cell cells[32];
291 	void SetupNextQuery(const char *columnName,DataStructures::Table::FilterQueryType op);
292 };
293 
294 
295 struct NetworkedQuickJoinUser
296 {
NetworkedQuickJoinUserNetworkedQuickJoinUser297 	NetworkedQuickJoinUser() {timeout=60000; minimumPlayers=2;}
298 
299 	// How long to wait for
300 	RakNetTime timeout;
301 	// What queries to join the room on.
302 	RoomQuery query;
303 	// Minimum number of slots to join
304 	int minimumPlayers;
305 
306 	void Serialize(bool writeToBitstream, RakNet::BitStream *bitStream);
307 };
308 
309 struct QuickJoinUser
310 {
311 	QuickJoinUser();
312 	~QuickJoinUser();
313 
314 	NetworkedQuickJoinUser networkedQuickJoinUser;
315 
316 	// Total amount of time spent waiting
317 	RakNetTime totalTimeWaiting;
318 
319 	// Which user
320 	RoomsParticipant* roomsParticipant;
321 	static int SortByTotalTimeWaiting( QuickJoinUser* const &key, QuickJoinUser* const &data );
322 	static int SortByMinimumSlots( QuickJoinUser* const &key, QuickJoinUser* const &data );
323 };
324 
325 int RoomPriorityComp( Room * const &key, Room * const &data );
326 
327 // PerGameRoomsContainer, mapped by game id
328 class AllGamesRoomsContainer
329 {
330 	public:
331 	AllGamesRoomsContainer();
332 	~AllGamesRoomsContainer();
333 	static void UnitTest(void);
334 
335 	RoomsErrorCode CreateRoom(RoomCreationParameters *roomCreationParameters,
336 		ProfanityFilter *profanityFilter);
337 
338 	// Enters a room based on the search queries. If no rooms are available to join, will create a room instead
339 	RoomsErrorCode EnterRoom(RoomCreationParameters *roomCreationParameters,
340 		RoomMemberMode roomMemberMode,
341 		ProfanityFilter *profanityFilter,
342 		RoomQuery *query,
343 		JoinedRoomResult *joinRoomResult);
344 
345 	// Attempts to join a room by search query filters
346 	// Returns REC_JOIN_BY_FILTER_*
347 	RoomsErrorCode JoinByFilter(GameIdentifier gameIdentifier, RoomMemberMode roomMemberMode, RoomsParticipant* roomsParticipant, RoomID lastRoomJoined, RoomQuery *query, JoinedRoomResult *joinRoomResult);
348 
349 	// Add a new title to host games with
350 	RoomsErrorCode AddTitle(GameIdentifier gameIdentifier);
351 
352 	// Get all pending invites to you
353 	RoomsErrorCode GetInvitesToParticipant(RoomsParticipant* roomsParticipant, DataStructures::List<InvitedUser*> &invites);
354 
355 	RoomsErrorCode RemoveUser(RoomsParticipant* roomsParticipant, RemoveUserResult *removeMemberResult);
356 
357 	// ROOMS OPERATIONS, implicit room
358 	RoomsErrorCode SendInvite(RoomsParticipant* roomsParticipant, RoomsParticipant* inviteeId, bool inviteToSpectatorSlot, RakNet::RakString subject, RakNet::RakString body);
359 	RoomsErrorCode AcceptInvite(RoomID roomId, Room **room, RoomsParticipant* roomsParticipant, RakNet::RakString inviteSender);
360 	RoomsErrorCode StartSpectating(RoomsParticipant* roomsParticipant);
361 	RoomsErrorCode StopSpectating(RoomsParticipant* roomsParticipant);
362 	RoomsErrorCode GrantModerator(RoomsParticipant* roomsParticipant, RoomsParticipant *newModerator, DataStructures::List<InvitedUser> &clearedInvites);
363 	RoomsErrorCode ChangeSlotCounts(RoomsParticipant* roomsParticipant, Slots slots);
364 	RoomsErrorCode SetCustomRoomProperties(RoomsParticipant* roomsParticipant, DataStructures::Table *table);
365 	RoomsErrorCode ChangeRoomName(RoomsParticipant* roomsParticipant, RakNet::RakString newRoomName, ProfanityFilter *profanityFilter);
366 	RoomsErrorCode SetHiddenFromSearches(RoomsParticipant* roomsParticipant, bool _hiddenFromSearches);
367 	RoomsErrorCode SetDestroyOnModeratorLeave(RoomsParticipant* roomsParticipant, bool destroyOnModeratorLeave);
368 	RoomsErrorCode SetReadyStatus(RoomsParticipant* roomsParticipant, bool isReady);
369 	RoomsErrorCode GetReadyStatus( RoomID roomId, Room **room, DataStructures::List<RoomsParticipant*> &readyUsers, DataStructures::List<RoomsParticipant*> &unreadyUsers);
370 	RoomsErrorCode SetRoomLockState(RoomsParticipant* roomsParticipant, RoomLockState _roomLockState);
371 	RoomsErrorCode GetRoomLockState(RoomID roomId, Room **room, RoomLockState *roomLockState);
372 	RoomsErrorCode AreAllMembersReady(RoomID roomId, Room **room, bool *allReady);
373 	RoomsErrorCode KickMember(RoomsParticipant* roomsParticipant, RoomsParticipant *kickedParticipant, RakNet::RakString reason);
374 	RoomsErrorCode UnbanMember(RoomsParticipant* roomsParticipant, RakNet::RakString name);
375 	RoomsErrorCode GetBanReason( RoomID lobbyRoomId, Room **room, RakNet::RakString name, RakNet::RakString *reason);
376 	RoomsErrorCode LeaveRoom(RoomsParticipant* roomsParticipant, RemoveUserResult *removeUserResult);
377 	//RoomsErrorCode GetKickReason(RoomsParticipant* roomsParticipant, RakNet::RakString *kickReason);
378 
379 
380 	void GetRoomProperties(RoomID roomId, Room **room, DataStructures::Table *table);
381 
382 	// Quick join algorithm:
383 	//
384 	// -- ROOM JOIN --
385 	//
386 	// For all rooms:
387 	// 1. Clear all quickJoinWorkingList from all rooms
388 	// For all quick join members
389 	// 2. Use RoomPrioritySort to get all rooms they can potentially join
390 	// 3. For each of these rooms, record that this member can potentially join by storing a copy of the pointer into quickJoinWorkingList, if minimumPlayers => total room slots
391 	// For all rooms:
392 	// 4. For each room where there are enough potential quick join members to fill the room, join all those members at once. Remove these members from the quick join list. Go to 1.
393 	//
394 	// -- ROOM CREATE --
395 	//
396 	// 5. Sort quick join members by minimumPlayers, excluding members where minimumPlayers > total number of quick join members
397 	// From greatest minimumPlayers to least
398 	// 6. If the current member created a room, find out how many subsequent members would join based on the custom filter
399 	// 7. If this satisfies minimumPlayers, have that user create a room and those subsequent members join.
400 	//
401 	// -- EXPIRE
402 	//
403 	// 5. Remove from list if timeout has expired.
404 	// 6. Return results of operation (List<timeoutExpired>, List<joinedARoom>, List<RoomsThatWereJoined>
405 	//
406 	// Returns false if processing skipped due to optimization timer
407 	RoomsErrorCode ProcessQuickJoins(
408 		DataStructures::List<QuickJoinUser*> &timeoutExpired,
409 		DataStructures::List<JoinedRoomResult> &joinedRoomMembers,
410 		DataStructures::List<QuickJoinUser*> &dereferencedPointers,
411 		RakNetTime elapsedTime);
412 
413 	// Quick join - Store a list of all members waiting to quick join.
414 	// Quick join ends when
415 	// 1. An existing room can be fully populated using waiting quick join members.
416 	// 2. Enough quick join members are waiting that a new room can be created with the number of members >= minimumPlayers for all members
417 	// It also ends if timeToWaitMS expires.
418 	// Returns REC_ADD_TO_QUICK_JOIN_*
419 	// Passed pointer is stored on REC_SUCCESS, allocate, and do not deallocate unless not successful
420 	RoomsErrorCode AddUserToQuickJoin(GameIdentifier gameIdentifier, QuickJoinUser *quickJoinMember);
421 
422 	// Returns REC_REMOVE_FROM_QUICK_JOIN_*
423 	RoomsErrorCode RemoveUserFromQuickJoin(RoomsParticipant* roomsParticipant, QuickJoinUser **qju);
424 
425 	// Is this user in quick join?
426 	bool IsInQuickJoin(RoomsParticipant* roomsParticipant);
427 
428 	// Get all rooms for a certain title
429 	static int RoomsSortByName( Room* const &key, Room* const &data );
430 	RoomsErrorCode SearchByFilter( GameIdentifier gameIdentifier, RoomsParticipant* roomsParticipant, RoomQuery *roomQuery, DataStructures::OrderedList<Room*, Room*, RoomsSortByName> &roomsOutput, bool onlyJoinable );
431 
432 	// Deallocate a room
433 	void DestroyRoomIfDead(Room *room);
434 
435 	// If a handle changes, you have to tell the system here. Otherwise ban and invite names will be out of synch
436 	// System does not verify that the handle is not currently in use since it does not necessarily know about all online players
437 	// This is an invariant up to the caller to uphold. Failure to do so will result in the wrong players being banned or invited
438 	void ChangeHandle(RakNet::RakString oldHandle, RakNet::RakString newHandle);
439 
440 	unsigned int GetPropertyIndex(RoomID lobbyRoomId, const char *propertyName) const;
441 
442 	DataStructures::Map<GameIdentifier, PerGameRoomsContainer*> perGamesRoomsContainers;
443 
444 	Room * GetRoomByLobbyRoomID(RoomID lobbyRoomID);
445 	Room * GetRoomByName(RakNet::RakString roomName);
446 
447 protected:
448 	RoomID nextRoomId;
449 };
450 
451 class PerGameRoomsContainer
452 {
453 public:
454 	PerGameRoomsContainer();
455 	~PerGameRoomsContainer();
456 
457 	// Has pointer column to class Room
458 	DataStructures::Table roomsTable;
459 
460 	// Members that are waiting to quick join
461 	DataStructures::List<QuickJoinUser*> quickJoinList;
462 
463 	static int RoomsSortByTimeThenTotalSlots( Room* const &key, Room* const &data );
464 
465 	protected:
466 
467 	RoomsErrorCode CreateRoom(RoomCreationParameters *roomCreationParameters,
468 		ProfanityFilter *profanityFilter,
469 		RoomID lobbyRoomId,
470 		bool validate);
471 	RoomsErrorCode LeaveRoom(RoomsParticipant* roomsParticipant, bool *gotNewModerator);
472 	RoomsErrorCode JoinByFilter(RoomMemberMode roomMemberMode, RoomsParticipant* roomsParticipant, RoomID lastRoomJoined, RoomQuery *query, JoinedRoomResult *joinRoomResult);
473 	RoomsErrorCode AddUserToQuickJoin(QuickJoinUser *quickJoinMember);
474 	RoomsErrorCode RemoveUserFromQuickJoin(RoomsParticipant* roomsParticipant, QuickJoinUser **qju);
475 	bool IsInQuickJoin(RoomsParticipant* roomsParticipant);
476 	unsigned int GetQuickJoinIndex(RoomsParticipant* roomsParticipant);
477 	void GetRoomNames(DataStructures::List<RakNet::RakString> &roomNames);
478 	void GetAllRooms(DataStructures::List<Room*> &rooms);
479 	// Looks for a particular room that has a particular ID
480 	Room * GetRoomByLobbyRoomID(RoomID lobbyRoomID);
481 	Room * GetRoomByName(RakNet::RakString roomName);
482 	RoomsErrorCode GetInvitesToParticipant(RoomsParticipant* roomsParticipant, DataStructures::List<InvitedUser*> &invites);
483 	void DestroyRoomIfDead(Room *room);
484 	void ChangeHandle(RakNet::RakString oldHandle, RakNet::RakString newHandle);
485 
486 	unsigned ProcessQuickJoins( DataStructures::List<QuickJoinUser*> &timeoutExpired,
487 		DataStructures::List<JoinedRoomResult> &joinedRooms,
488 		DataStructures::List<QuickJoinUser*> &dereferencedPointers,
489 		RakNetTime elapsedTime,
490 		RoomID startingRoomId);
491 
492 	// Sort an input list of rooms
493 	// Rooms are sorted by time created (longest is higher priority). If within one minute, then subsorted by total playable slot count (lower is higher priority).
494 	// When using EnterRoom or JoinByFilter, record the last roomOutput joined, and try to avoid rejoining the same roomOutput just left
495 	void RoomPrioritySort( RoomsParticipant* roomsParticipant, RoomQuery *roomQuery, DataStructures::OrderedList<Room*, Room*, RoomsSortByTimeThenTotalSlots> &roomsOutput );
496 
497 	RoomsErrorCode SearchByFilter( RoomsParticipant* roomsParticipant, RoomQuery *roomQuery, DataStructures::OrderedList<Room*, Room*, AllGamesRoomsContainer::RoomsSortByName> &roomsOutput, bool onlyJoinable );
498 
499 	friend class AllGamesRoomsContainer;
500 	IntervalTimer nextQuickJoinProcess;
501 };
502 
503 // Holds all the members of a particular roomOutput
504 class Room
505 {
506 	public:
507 		Room( RoomID _roomId, RoomCreationParameters *roomCreationParameters, DataStructures::Table::Row *_row, RoomsParticipant* roomsParticipant );
508 		~Room();
509 		RoomsErrorCode SendInvite(RoomsParticipant* roomsParticipant, RoomsParticipant* inviteeId, bool inviteToSpectatorSlot, RakNet::RakString subject, RakNet::RakString body);
510 		RoomsErrorCode AcceptInvite(RoomsParticipant* roomsParticipant, RakNet::RakString inviteSender);
511 		RoomsErrorCode StartSpectating(RoomsParticipant* roomsParticipant);
512 		RoomsErrorCode StopSpectating(RoomsParticipant* roomsParticipant);
513 		RoomsErrorCode GrantModerator(RoomsParticipant* roomsParticipant, RoomsParticipant *newModerator, DataStructures::List<InvitedUser> &clearedInvites);
514 		RoomsErrorCode ChangeSlotCounts(RoomsParticipant* roomsParticipant, Slots slots);
515 		RoomsErrorCode SetCustomRoomProperties(RoomsParticipant* roomsParticipant, DataStructures::Table *table);
516 		RoomsErrorCode ChangeRoomName(RoomsParticipant* roomsParticipant, RakNet::RakString newRoomName, ProfanityFilter *profanityFilter);
517 		RoomsErrorCode SetHiddenFromSearches(RoomsParticipant* roomsParticipant, bool _hiddenFromSearches);
518 		RoomsErrorCode SetDestroyOnModeratorLeave(RoomsParticipant* roomsParticipant, bool destroyOnModeratorLeave);
519 		RoomsErrorCode SetReadyStatus(RoomsParticipant* roomsParticipant, bool isReady);
520 		RoomsErrorCode GetReadyStatus(DataStructures::List<RoomsParticipant*> &readyUsers, DataStructures::List<RoomsParticipant*> &unreadyUsers);
521 		RoomsErrorCode SetRoomLockState(RoomsParticipant* roomsParticipant, RoomLockState _roomLockState);
522 		RoomsErrorCode GetRoomLockState(RoomLockState *_roomLockState);
523 		RoomsErrorCode AreAllMembersReady(unsigned int exceptThisIndex, bool *allReady);
524 		RoomsErrorCode KickMember(RoomsParticipant* roomsParticipant, RoomsParticipant *kickedParticipant, RakNet::RakString reason);
525 		RoomsErrorCode UnbanMember(RoomsParticipant* roomsParticipant, RakNet::RakString name);
526 		RoomsErrorCode GetBanReason(RakNet::RakString name, RakNet::RakString *reason);
527 		RoomsErrorCode LeaveRoom(RoomsParticipant* roomsParticipant, RemoveUserResult *removeUserResult);
528 		//RoomsErrorCode GetKickReason(RoomsParticipant* roomsParticipant, RakNet::RakString *kickReason);
529 
530 		RoomsErrorCode JoinByFilter(RoomsParticipant* roomsParticipant, RoomMemberMode roomMemberMode, JoinedRoomResult *joinRoomResult);
531 		RoomsErrorCode JoinByQuickJoin(RoomsParticipant* roomsParticipant, RoomMemberMode roomMemberMode, JoinedRoomResult *joinRoomResult);
532 
533 		bool IsHiddenToParticipant(RoomsParticipant* roomsParticipant) const;
534 
535 		// Can this user join this roomOutput?
536 		ParticipantCanJoinRoomResult ParticipantCanJoinAsPlayer( RoomsParticipant* roomsParticipant, bool asSpectator, bool checkHasInvite );
537 		ParticipantCanJoinRoomResult ParticipantCanJoinRoom( RoomsParticipant* roomsParticipant, bool asSpectator, bool checkHasInvite );
538 
539 		// Returns true if there are only spectators, or nobody at all
540 		bool IsRoomDead(void) const;
541 
542 		RoomsErrorCode GetInvitesToParticipant(RoomsParticipant* roomsParticipant, DataStructures::List<InvitedUser*> &invites);
543 
544 		RoomsParticipant* GetModerator(void) const;
545 
546 		//  Gets the roomOutput ID
547 		RoomID GetID(void) const;
548 
549 		double GetNumericProperty(RoomID lobbyRoomId, const char *propertyName) const;
550 		const char *GetStringProperty(RoomID lobbyRoomId, const char *propertyName) const;
551 
552 		double GetNumericProperty(int index) const;
553 		const char *GetStringProperty(int index) const;
554 		void SetNumericProperty(int index, double value);
555 		void SetStringProperty(int index, const char *value);
556 
557 		// Public for easy access
558 		DataStructures::List<RoomMember*> roomMemberList;
559 
560 		DataStructures::List<InvitedUser> inviteList;
561 		DataStructures::List<BannedUser> banList;
562 
563 		// Don't store - slow because when removing users I have to iterate through every room
564 		// DataStructures::List<KickedUser> kickedList;
565 
566 		// Internal
567 		DataStructures::List<QuickJoinUser*> quickJoinWorkingList;
568 
569 		static void UpdateRowSlots( DataStructures::Table::Row* row, Slots *totalSlots, Slots *usedSlots);
570 
571 		void ChangeHandle(RakNet::RakString oldHandle, RakNet::RakString newHandle);
572 protected:
573 		Room();
574 
575 		// Updates the table row
576 		RoomsErrorCode RemoveUser(RoomsParticipant* roomsParticipant,RemoveUserResult *removeUserResult);
577 
578 		bool IsRoomLockedToSpectators(void) const;
579 		bool IsRoomLockedToPlayers(void) const;
580 
581 		bool IsInRoom(RoomsParticipant* roomsParticipant) const;
582 		bool HasInvite(RakNet::RakString roomsParticipant);
583 		unsigned int GetRoomIndex(RoomsParticipant* roomsParticipant) const;
584 		unsigned int GetBannedIndex(RakNet::RakString username) const;
585 		unsigned int GetInviteIndex(RakNet::RakString invitee, RakNet::RakString invitor) const;
586 		unsigned int GetFirstInviteIndex(RakNet::RakString invitee) const;
587 		bool AreAllPlayableSlotsFilled(void) const;
588 		bool HasOpenPublicSlots(void) const;
589 		bool HasOpenReservedSlots(void) const;
590 		bool HasOpenSpectatorSlots(void) const;
591 		void UpdateUsedSlots( void );
592 		void UpdateUsedSlots( Slots *totalSlots, Slots *usedSlots );
593 		static void UpdateUsedSlots( DataStructures::Table::Row* tableRow, Slots *totalSlots, Slots *usedSlots );
594 		Slots GetTotalSlots(void) const;
595 		void SetTotalSlots(Slots *totalSlots);
596 		Slots GetUsedSlots(void) const;
597 
598 
599 		RoomLockState roomLockState;
600 
601 		friend struct RoomDescriptor;
602 		friend class PerGameRoomsContainer;
603 		friend class AllGamesRoomsContainer;
604 
605 		RoomID lobbyRoomId;
606 		DataStructures::Table::Row *tableRow;
607 
608 		bool autoLockReadyStatus;
609 		bool hiddenFromSearches;
610 //		bool destroyOnModeratorLeave;
611 		bool clearInvitesOnNewModerator;
612 		NetworkedRoomCreationParameters::SendInvitePermission inviteToRoomPermission, inviteToSpectatorSlotPermission;
613 
614 		bool roomDestroyed;
615 
616 };
617 
618 
619 } // namespace Lobby2
620 
621 #endif
622