1 
2 #include "Config.hpp"
3 
4 #ifdef USE_EOS
5 
6 #ifdef WINDOWS
7 #define STRINGIZE2(s) #s
8 #define STRINGIZE(s) STRINGIZE2(s)
9 #define BUILD_ENV_PR STRINGIZE(BUILD_PR)
10 #define BUILD_ENV_SA STRINGIZE(BUILD_SA)
11 #define BUILD_ENV_DE STRINGIZE(BUILD_DE)
12 #define BUILD_ENV_CC STRINGIZE(BUILD_CC)
13 #define BUILD_ENV_CS STRINGIZE(BUILD_CS)
14 #define BUILD_ENV_GSE STRINGIZE(BUILD_GSE)
15 #endif
16 
17 #include "main.hpp"
18 #include "menu.hpp"
19 #include "game.hpp"
20 #include "eos.hpp"
21 #include "json.hpp"
22 #include "scores.hpp"
23 #include "files.hpp"
24 #include "draw.hpp"
25 #include "interface/interface.hpp"
26 #include "interface/ui.hpp"
27 #include "lobbies.hpp"
28 
29 EOSFuncs EOS;
30 
LobbyLeaveCleanup(EOSFuncs::LobbyData_t & lobby)31 void LobbyLeaveCleanup(EOSFuncs::LobbyData_t& lobby)
32 {
33 	EOS.P2PConnectionInfo.resetPeersAndServerData();
34 	lobby.bAwaitingLeaveCallback = false;
35 	lobby.UnsubscribeFromLobbyUpdates();
36 	lobby.ClearData();
37 }
38 
LoggingCallback(const EOS_LogMessage * log)39 void EOS_CALL EOSFuncs::LoggingCallback(const EOS_LogMessage* log)
40 {
41 	printlog("[EOS Logging]: %s:%s", log->Category, log->Message);
42 }
AuthLoginCompleteCallback(const EOS_Auth_LoginCallbackInfo * data)43 void EOS_CALL EOSFuncs::AuthLoginCompleteCallback(const EOS_Auth_LoginCallbackInfo* data)
44 {
45 	EOS_HAuth AuthHandle = EOS_Platform_GetAuthInterface(EOS.PlatformHandle);
46 	EOS.AccountManager.waitingForCallback = false;
47 	if ( !data )
48 	{
49 		EOSFuncs::logError("Login Callback error: null data");
50 	}
51 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
52 	{
53 		EOSFuncs::logInfo("Login Callback: success");
54 		EOS.AccountManager.AccountAuthenticationStatus = EOS_EResult::EOS_Success;
55 
56 		const int numAccounts = EOS_Auth_GetLoggedInAccountsCount(AuthHandle);
57 		for ( int accIndex = 0; accIndex < numAccounts; ++accIndex )
58 		{
59 			EOS.CurrentUserInfo.epicAccountId = EOSFuncs::Helpers_t::epicIdToString(EOS_Auth_GetLoggedInAccountByIndex(AuthHandle, accIndex));
60 			EOS_ELoginStatus LoginStatus;
61 			LoginStatus = EOS_Auth_GetLoginStatus(AuthHandle, data->LocalUserId);
62 
63 			EOSFuncs::logInfo("Account index: %d Status: %d UserID: %s", accIndex,
64 				static_cast<int>(LoginStatus), EOS.CurrentUserInfo.epicAccountId.c_str());
65 
66 			EOS.getUserInfo(EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()),
67 				UserInfoQueryType::USER_INFO_QUERY_LOCAL, accIndex);
68 			EOS.initConnectLogin();
69 			EOS.queryDLCOwnership();
70 		}
71 		return;
72 	}
73 	else if ( data->ResultCode == EOS_EResult::EOS_OperationWillRetry )
74 	{
75 		EOSFuncs::logError("Login Callback: retrying");
76 		return;
77 	}
78 	else if ( data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode )
79 	{
80 		EOSFuncs::logError("Login Callback: PIN required");
81 	}
82 	else if ( data->ResultCode == EOS_EResult::EOS_Auth_MFARequired )
83 	{
84 		EOSFuncs::logError("Login Callback: MFA required");
85 	}
86 	else
87 	{
88 		EOSFuncs::logError("Login Callback: General fail: %d", static_cast<int>(data->ResultCode));
89 		EOS.AccountManager.AccountAuthenticationStatus = data->ResultCode;
90 		return;
91 	}
92 	EOS.AccountManager.AccountAuthenticationStatus = EOS_EResult::EOS_InvalidAuth;
93 }
94 
ConnectLoginCompleteCallback(const EOS_Connect_LoginCallbackInfo * data)95 void EOS_CALL EOSFuncs::ConnectLoginCompleteCallback(const EOS_Connect_LoginCallbackInfo* data)
96 {
97 	if ( !data )
98 	{
99 		EOSFuncs::logError("Connect Login Callback: null data");
100 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
101 		EOS.CurrentUserInfo.bUserLoggedIn = false;
102 		return;
103 	}
104 
105 	if ( data->ResultCode == EOS_EResult::EOS_Success )
106 	{
107 		EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId);
108 		EOS.CurrentUserInfo.bUserLoggedIn = true;
109 		EOS.SubscribeToConnectionRequests();
110 		EOSFuncs::logInfo("Connect Login Callback success: %s", EOS.CurrentUserInfo.getProductUserIdStr());
111 
112 		// load achievement data
113 		if ( !EOS.Achievements.bAchievementsInit )
114 		{
115 			EOS.loadAchievementData();
116 		}
117 	}
118 	else if ( data->ResultCode == EOS_EResult::EOS_InvalidUser )
119 	{
120 		EOSFuncs::logInfo("Connect Login Callback: creating new user");
121 		EOS_Connect_CreateUserOptions CreateUserOptions;
122 		CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST;
123 		CreateUserOptions.ContinuanceToken = data->ContinuanceToken;
124 
125 		EOS.ConnectHandle = EOS_Platform_GetConnectInterface(EOS.PlatformHandle);
126 		EOS_Connect_CreateUser(EOS.ConnectHandle, &CreateUserOptions, nullptr, OnCreateUserCallback);
127 	}
128 	else
129 	{
130 		EOSFuncs::logError("Connect Login Callback: General fail: %d", static_cast<int>(data->ResultCode));
131 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
132 		EOS.CurrentUserInfo.bUserLoggedIn = false;
133 	}
134 }
135 
ConnectLoginCrossplayCompleteCallback(const EOS_Connect_LoginCallbackInfo * data)136 void EOS_CALL EOSFuncs::ConnectLoginCrossplayCompleteCallback(const EOS_Connect_LoginCallbackInfo* data)
137 {
138 	EOS.CrossplayAccountManager.awaitingConnectCallback = false;
139 
140 	if ( !data )
141 	{
142 		EOSFuncs::logError("Crossplay Connect Login Callback: null data");
143 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
144 		EOS.CurrentUserInfo.bUserLoggedIn = false;
145 		return;
146 	}
147 
148 	EOS.CrossplayAccountManager.connectLoginStatus = data->ResultCode;
149 
150 	if ( data->ResultCode == EOS_EResult::EOS_Success )
151 	{
152 		EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId);
153 		EOS.CurrentUserInfo.bUserLoggedIn = true;
154 		EOS.SubscribeToConnectionRequests();
155 		EOS.AddConnectAuthExpirationNotification();
156 #ifdef STEAMWORKS
157 		EOS_ELoginStatus authLoginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle),
158 			EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()));
159 		if ( authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn )
160 		{
161 			EOS.queryLocalExternalAccountId(EOS_EExternalAccountType::EOS_EAT_STEAM);
162 		}
163 		EOS.StatGlobalManager.queryGlobalStatUser();
164 #endif
165 		EOSFuncs::logInfo("Crossplay Connect Login Callback success: %s", EOS.CurrentUserInfo.getProductUserIdStr());
166 	}
167 	else if ( data->ResultCode == EOS_EResult::EOS_InvalidUser )
168 	{
169 		if ( EOS.CrossplayAccountManager.acceptedEula )
170 		{
171 			EOS_Connect_CreateUserOptions CreateUserOptions;
172 			CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST;
173 			CreateUserOptions.ContinuanceToken = data->ContinuanceToken;
174 
175 			EOS.ConnectHandle = EOS_Platform_GetConnectInterface(EOS.PlatformHandle);
176 			EOS_Connect_CreateUser(EOS.ConnectHandle, &CreateUserOptions, nullptr, OnCreateUserCrossplayCallback);
177 		}
178 		else
179 		{
180 			EOS.CrossplayAccountManager.continuanceToken = data->ContinuanceToken;
181 		}
182 	}
183 	else
184 	{
185 		EOSFuncs::logError("Crossplay Connect Login Callback: General fail: %d", static_cast<int>(data->ResultCode));
186 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
187 		EOS.CurrentUserInfo.bUserLoggedIn = false;
188 	}
189 }
190 
OnCreateUserCallback(const EOS_Connect_CreateUserCallbackInfo * data)191 void EOS_CALL EOSFuncs::OnCreateUserCallback(const EOS_Connect_CreateUserCallbackInfo* data)
192 {
193 	if ( !data )
194 	{
195 		EOSFuncs::logError("OnCreateUserCallback: null data");
196 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
197 		EOS.CurrentUserInfo.bUserLoggedIn = false;
198 		return;
199 	}
200 	if ( data->ResultCode == EOS_EResult::EOS_Success )
201 	{
202 		EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId);
203 		EOS.CurrentUserInfo.bUserLoggedIn = true;
204 		EOS.SubscribeToConnectionRequests();
205 		EOSFuncs::logInfo("OnCreateUserCallback success, new user: %s", EOS.CurrentUserInfo.getProductUserIdStr());
206 
207 		// load achievement data
208 		if ( !EOS.Achievements.bAchievementsInit )
209 		{
210 			EOS.loadAchievementData();
211 		}
212 	}
213 	else
214 	{
215 		EOSFuncs::logError("OnCreateUserCallback: General fail: %d", static_cast<int>(data->ResultCode));
216 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
217 		EOS.CurrentUserInfo.bUserLoggedIn = false;
218 	}
219 }
220 
OnCreateUserCrossplayCallback(const EOS_Connect_CreateUserCallbackInfo * data)221 void EOS_CALL EOSFuncs::OnCreateUserCrossplayCallback(const EOS_Connect_CreateUserCallbackInfo* data)
222 {
223 	EOS.CrossplayAccountManager.connectLoginStatus = EOS_EResult::EOS_NotConfigured;
224 	if ( !data )
225 	{
226 		EOSFuncs::logError("OnCreateUserCrossplayCallback: null data");
227 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
228 		EOS.CurrentUserInfo.bUserLoggedIn = false;
229 		return;
230 	}
231 	if ( data->ResultCode == EOS_EResult::EOS_Success )
232 	{
233 		EOS.CurrentUserInfo.setProductUserIdHandle(data->LocalUserId);
234 		EOS.CurrentUserInfo.bUserLoggedIn = true;
235 		EOS.SubscribeToConnectionRequests();
236 		EOS.AddConnectAuthExpirationNotification();
237 #ifdef STEAMWORKS
238 		EOS.CrossplayAccountManager.connectLoginStatus = EOS_EResult::EOS_Success;
239 		EOS_ELoginStatus authLoginStatus = EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle),
240 			EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()));
241 		if ( authLoginStatus != EOS_ELoginStatus::EOS_LS_LoggedIn )
242 		{
243 			EOS.queryLocalExternalAccountId(EOS_EExternalAccountType::EOS_EAT_STEAM);
244 		}
245 #endif
246 		EOSFuncs::logInfo("OnCreateUserCrossplayCallback success, new user: %s", EOS.CurrentUserInfo.getProductUserIdStr());
247 	}
248 	else
249 	{
250 		EOS.CrossplayAccountManager.connectLoginStatus = data->ResultCode;
251 		EOSFuncs::logError("OnCreateUserCrossplayCallback: General fail: %d", static_cast<int>(data->ResultCode));
252 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
253 		EOS.CurrentUserInfo.bUserLoggedIn = false;
254 	}
255 }
256 
FriendsQueryCallback(const EOS_Friends_QueryFriendsCallbackInfo * data)257 void EOS_CALL EOSFuncs::FriendsQueryCallback(const EOS_Friends_QueryFriendsCallbackInfo* data)
258 {
259 	if ( !data )
260 	{
261 		EOSFuncs::logError("FriendsQueryCallback: null data");
262 		return;
263 	}
264 	EOS_HFriends FriendsHandle = EOS_Platform_GetFriendsInterface(EOS.PlatformHandle);
265 	EOS_Friends_GetFriendsCountOptions FriendsCountOptions;
266 	FriendsCountOptions.ApiVersion = EOS_FRIENDS_GETFRIENDSCOUNT_API_LATEST;
267 	FriendsCountOptions.LocalUserId = data->LocalUserId;
268 	int numFriends = EOS_Friends_GetFriendsCount(FriendsHandle, &FriendsCountOptions);
269 	EOSFuncs::logInfo("FriendsQueryCallback: Friends num: %d", numFriends);
270 
271 	EOS.CurrentUserInfo.Friends.clear();
272 
273 	EOS_Friends_GetFriendAtIndexOptions IndexOptions;
274 	IndexOptions.ApiVersion = EOS_FRIENDS_GETFRIENDATINDEX_API_LATEST;
275 	IndexOptions.LocalUserId = data->LocalUserId;
276 	for ( int i = 0; i < numFriends; ++i )
277 	{
278 		IndexOptions.Index = i;
279 		EOS_EpicAccountId FriendUserId = EOS_Friends_GetFriendAtIndex(FriendsHandle, &IndexOptions);
280 
281 		if ( EOSFuncs::Helpers_t::epicIdIsValid(FriendUserId) )
282 		{
283 			EOS_Friends_GetStatusOptions StatusOptions;
284 			StatusOptions.ApiVersion = EOS_FRIENDS_GETSTATUS_API_LATEST;
285 			StatusOptions.LocalUserId = data->LocalUserId;
286 			StatusOptions.TargetUserId = FriendUserId;
287 			EOS_EFriendsStatus FriendStatus = EOS_Friends_GetStatus(FriendsHandle, &StatusOptions);
288 
289 			EOSFuncs::logInfo("FriendsQueryCallback: FriendStatus: Id: %s Status: %d", EOSFuncs::Helpers_t::epicIdToString(FriendUserId), static_cast<int>(FriendStatus));
290 
291 			FriendInfo_t newFriend(FriendStatus, EOSFuncs::Helpers_t::epicIdToString(FriendUserId));
292 			EOS.CurrentUserInfo.Friends.push_back(newFriend);
293 			EOS.getUserInfo(FriendUserId, EOS.USER_INFO_QUERY_FRIEND, i);
294 		}
295 		else
296 		{
297 			EOSFuncs::logError("FriendsQueryCallback: Friend ID was invalid!");
298 		}
299 	}
300 }
301 
UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbackInfo * data)302 void EOS_CALL EOSFuncs::UserInfoCallback(const EOS_UserInfo_QueryUserInfoCallbackInfo* data)
303 {
304 	if ( !data )
305 	{
306 		EOSFuncs::logError("UserInfoCallback: null data");
307 		return;
308 	}
309 	UserInfoQueryData_t* userInfoQueryData = (static_cast<UserInfoQueryData_t*>(data->ClientData));
310 	if ( data->ResultCode == EOS_EResult::EOS_Success )
311 	{
312 		EOS_UserInfo_CopyUserInfoOptions UserInfoOptions;
313 		UserInfoOptions.ApiVersion = EOS_USERINFO_COPYUSERINFO_API_LATEST;
314 		UserInfoOptions.LocalUserId = data->LocalUserId;
315 		UserInfoOptions.TargetUserId = data->TargetUserId;
316 		EOS_UserInfo* userInfo; // result is managed by application to free the memory.
317 		if ( EOS_UserInfo_CopyUserInfo(EOS_Platform_GetUserInfoInterface(EOS.PlatformHandle),
318 			&UserInfoOptions, &userInfo) == EOS_EResult::EOS_Success )
319 		{
320 			if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOCAL )
321 			{
322 				// local user
323 				EOS.CurrentUserInfo.Name = userInfo->DisplayName;
324 				EOS.CurrentUserInfo.bUserInfoRequireUpdate = false;
325 				EOSFuncs::logInfo("UserInfoCallback: Current User Name: %s", userInfo->DisplayName);
326 			}
327 			else if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_FRIEND )
328 			{
329 				if ( EOS.CurrentUserInfo.Friends.empty() )
330 				{
331 					EOSFuncs::logInfo("UserInfoCallback: friend info request failed due empty friends list");
332 				}
333 				else
334 				{
335 					bool foundFriend = false;
336 					std::string queryTarget = EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId);
337 					for ( auto& it : EOS.CurrentUserInfo.Friends )
338 					{
339 						if ( it.EpicAccountId.compare(queryTarget) == 0 )
340 						{
341 							foundFriend = true;
342 							it.Name = userInfo->DisplayName;
343 							it.bUserInfoRequireUpdate = false;
344 							EOSFuncs::logInfo("UserInfoCallback: found friend username: %s", userInfo->DisplayName);
345 							break;
346 						}
347 					}
348 					if ( !foundFriend )
349 					{
350 						EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s",
351 							EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId));
352 					}
353 				}
354 			}
355 			else if ( userInfoQueryData->queryType == EOS.USER_INFO_QUERY_LOBBY_MEMBER )
356 			{
357 				if ( !EOS.CurrentLobbyData.currentLobbyIsValid() || EOS.CurrentLobbyData.playersInLobby.empty() )
358 				{
359 					EOSFuncs::logInfo("UserInfoCallback: lobby member request failed due to invalid or no player data in lobby");
360 				}
361 				else
362 				{
363 					bool foundMember = false;
364 					std::string queryTarget = EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId);
365 					for ( auto& it : EOS.CurrentLobbyData.playersInLobby )
366 					{
367 						if ( queryTarget.compare(it.memberEpicAccountId.c_str()) == 0 )
368 						{
369 							foundMember = true;
370 							it.name = userInfo->DisplayName;
371 							it.bUserInfoRequireUpdate = false;
372 							EOSFuncs::logInfo("UserInfoCallback: found lobby username: %s", userInfo->DisplayName);
373 							break;
374 						}
375 					}
376 					if ( !foundMember )
377 					{
378 						EOSFuncs::logInfo("UserInfoCallback: could not find player in current lobby with account %s",
379 							EOSFuncs::Helpers_t::epicIdToString(userInfoQueryData->epicAccountId));
380 					}
381 				}
382 			}
383 			EOS_UserInfo_Release(userInfo);
384 		}
385 		else
386 		{
387 			EOSFuncs::logError("UserInfoCallback: Error copying user info");
388 		}
389 	}
390 	delete userInfoQueryData;
391 }
392 
OnCreateLobbyFinished(const EOS_Lobby_CreateLobbyCallbackInfo * data)393 void EOS_CALL EOSFuncs::OnCreateLobbyFinished(const EOS_Lobby_CreateLobbyCallbackInfo* data)
394 {
395 	EOS.CurrentLobbyData.bAwaitingCreationCallback = false;
396 	if ( !data )
397 	{
398 		EOSFuncs::logError("OnCreateLobbyFinished: null data");
399 		return;
400 	}
401 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
402 	{
403 		EOS.CurrentLobbyData.LobbyId = data->LobbyId;
404 
405 		EOS.CurrentLobbyData.LobbyAttributes.lobbyName = EOS.CurrentUserInfo.Name + "'s lobby";
406 		strncpy(EOS.currentLobbyName, EOS.CurrentLobbyData.LobbyAttributes.lobbyName.c_str(), 31);
407 
408 		Uint32 keygen = rand() % (1679615 + 1); // limit of 'zzzz' as base-36 string
409 		EOS.CurrentLobbyData.LobbyAttributes.gameJoinKey = EOS.getLobbyCodeFromGameKey(keygen);
410 		std::chrono::system_clock::duration epochDuration = std::chrono::system_clock::now().time_since_epoch();
411 		EOS.CurrentLobbyData.LobbyAttributes.lobbyCreationTime = std::chrono::duration_cast<std::chrono::seconds>(epochDuration).count();
412 
413 		EOSFuncs::logInfo("OnCreateLobbyFinished: Generated game code %s", EOS.CurrentLobbyData.LobbyAttributes.gameJoinKey.c_str());
414 
415 		EOS.CurrentLobbyData.updateLobbyForHost(EOSFuncs::LobbyData_t::HostUpdateLobbyTypes::LOBBY_UPDATE_MAIN_MENU);
416 		EOS.CurrentLobbyData.SubscribeToLobbyUpdates();
417 	}
418 	else
419 	{
420 		EOSFuncs::logError("OnCreateLobbyFinished: Callback failure: %d", static_cast<int>(data->ResultCode));
421 	}
422 }
423 
OnLobbySearchFinished(const EOS_LobbySearch_FindCallbackInfo * data)424 void EOS_CALL EOSFuncs::OnLobbySearchFinished(const EOS_LobbySearch_FindCallbackInfo* data)
425 {
426 	EOS.bRequestingLobbies = false;
427 	if ( !data )
428 	{
429 		EOSFuncs::logError("OnLobbySearchFinished: null data");
430 		return;
431 	}
432 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
433 	{
434 		EOS_LobbySearch_GetSearchResultCountOptions SearchResultOptions;
435 		SearchResultOptions.ApiVersion = EOS_LOBBYSEARCH_GETSEARCHRESULTCOUNT_API_LATEST;
436 		int NumSearchResults = EOS_LobbySearch_GetSearchResultCount(EOS.LobbySearchResults.CurrentLobbySearch, &SearchResultOptions);
437 		int* searchOptions = static_cast<int*>(data->ClientData);
438 
439 		EOS_LobbySearch_CopySearchResultByIndexOptions IndexOptions;
440 		IndexOptions.ApiVersion = EOS_LOBBYSEARCH_COPYSEARCHRESULTBYINDEX_API_LATEST;
441 		for ( int i = 0; i < NumSearchResults; ++i )
442 		{
443 			LobbyData_t newLobby;
444 			EOS_HLobbyDetails LobbyDetails = nullptr;
445 			IndexOptions.LobbyIndex = i;
446 			EOS_EResult Result = EOS_LobbySearch_CopySearchResultByIndex(EOS.LobbySearchResults.CurrentLobbySearch,
447 				&IndexOptions, &LobbyDetails);
448 			if ( Result == EOS_EResult::EOS_Success && LobbyDetails )
449 			{
450 				EOS.setLobbyDetailsFromHandle(LobbyDetails, &newLobby);
451 				EOSFuncs::logInfo("OnLobbySearchFinished: Found lobby: %s, Owner: %s, MaxPlayers: %d",
452 					newLobby.LobbyId.c_str(), newLobby.OwnerProductUserId.c_str(), newLobby.MaxPlayers);
453 
454 				EOS.LobbySearchResults.results.push_back(newLobby);
455 
456 				if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS]
457 					== static_cast<int>(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT) )
458 				{
459 					// set the handle to be used for joining.
460 					EOS.LobbyParameters.lobbyToJoin = LobbyDetails;
461 					EOS.joinLobby(&newLobby);
462 				}
463 				else if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS]
464 					== static_cast<int>(EOSFuncs::LobbyParameters_t::LOBBY_DONT_JOIN) )
465 				{
466 					// we can release this handle.
467 					EOS_LobbyDetails_Release(LobbyDetails);
468 				}
469 				else if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS]
470 					== static_cast<int>(EOSFuncs::LobbyParameters_t::LOBBY_UPDATE_CURRENTLOBBY) )
471 				{
472 					// TODO need?
473 					EOS.setLobbyDetailsFromHandle(LobbyDetails, &EOS.CurrentLobbyData);
474 
475 					// we can release this handle.
476 					EOS_LobbyDetails_Release(LobbyDetails);
477 				}
478 			}
479 			else
480 			{
481 				EOS_LobbyDetails_Release(LobbyDetails);
482 			}
483 		}
484 
485 		if ( NumSearchResults == 0 )
486 		{
487 			EOSFuncs::logInfo("OnLobbySearchFinished: Found 0 lobbies!");
488 		}
489 		EOS.LobbySearchResults.sortResults();
490 		return;
491 	}
492 	else if ( data->ResultCode == EOS_EResult::EOS_NotFound )
493 	{
494 		EOSFuncs::logError("OnLobbySearchFinished: Requested lobby no longer exists", static_cast<int>(data->ResultCode));
495 	}
496 	else
497 	{
498 		EOSFuncs::logError("OnLobbySearchFinished: Callback failure: %d", static_cast<int>(data->ResultCode));
499 	}
500 
501 	int* searchOptions = static_cast<int*>(data->ClientData);
502 	if ( searchOptions[EOSFuncs::LobbyParameters_t::JOIN_OPTIONS]
503 		== static_cast<int>(EOSFuncs::LobbyParameters_t::LOBBY_JOIN_FIRST_SEARCH_RESULT) )
504 	{
505 		// we were trying to join a lobby, set error message.
506 		EOS.bConnectingToLobbyWindow = false;
507 		EOS.bConnectingToLobby = false;
508 		EOS.ConnectingToLobbyStatus = static_cast<int>(data->ResultCode);
509 	}
510 }
511 
OnLobbyJoinCallback(const EOS_Lobby_JoinLobbyCallbackInfo * data)512 void EOS_CALL EOSFuncs::OnLobbyJoinCallback(const EOS_Lobby_JoinLobbyCallbackInfo* data)
513 {
514 	if ( !data )
515 	{
516 		EOSFuncs::logError("OnLobbyJoinCallback: null data");
517 		EOS.bConnectingToLobby = false;
518 		EOS.bConnectingToLobbyWindow = false;
519 		EOS.ConnectingToLobbyStatus = static_cast<int>(EOS_EResult::EOS_UnexpectedError);
520 	}
521 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
522 	{
523 		EOSFuncs::logInfo("OnLobbyJoinCallback: Joined lobby id: %s", data->LobbyId);
524 		/*if ( static_cast<LobbyData_t*>(data->ClientData) == &EOS.CurrentLobbyData )
525 		{
526 			EOS.CurrentLobbyData.LobbyId = data->LobbyId;
527 		}*/
528 		EOS.bConnectingToLobby = false;
529 
530 		if ( EOS.CurrentLobbyData.bDenyLobbyJoinEvent && EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
531 		{
532 			// early return, immediately exit lobby.
533 			EOS.CurrentLobbyData.bDenyLobbyJoinEvent = false;
534 			EOS.leaveLobby();
535 			logInfo("OnLobbyJoinCallback: forcibly denying joining current lobby id: %s", EOS.CurrentLobbyData.LobbyId.c_str());
536 			return;
537 		}
538 
539 		EOS.ConnectingToLobbyStatus = static_cast<int>(data->ResultCode);
540 		EOS.searchLobbies(EOSFuncs::LobbyParameters_t::LobbySearchOptions::LOBBY_SEARCH_BY_LOBBYID,
541 			EOSFuncs::LobbyParameters_t::LobbyJoinOptions::LOBBY_UPDATE_CURRENTLOBBY, data->LobbyId);
542 
543 		EOS.P2PConnectionInfo.serverProductId = EOSFuncs::Helpers_t::productIdFromString(EOS.CurrentLobbyData.OwnerProductUserId.c_str());
544 		EOS.CurrentLobbyData.SubscribeToLobbyUpdates();
545 		EOS.P2PConnectionInfo.peerProductIds.clear();
546 		EOS.P2PConnectionInfo.insertProductIdIntoPeers(EOS.CurrentUserInfo.getProductUserIdHandle());
547 		return;
548 	}
549 	else
550 	{
551 		EOSFuncs::logError("OnLobbyJoinCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
552 		EOS.bConnectingToLobbyWindow = false;
553 		EOS.bConnectingToLobby = false;
554 		EOS.ConnectingToLobbyStatus = static_cast<int>(data->ResultCode);
555 	}
556 
557 	LobbyLeaveCleanup(EOS.CurrentLobbyData);
558 	EOS.LobbyParameters.clearLobbyToJoin();
559 }
560 
OnLobbyLeaveCallback(const EOS_Lobby_LeaveLobbyCallbackInfo * data)561 void EOS_CALL EOSFuncs::OnLobbyLeaveCallback(const EOS_Lobby_LeaveLobbyCallbackInfo* data)
562 {
563 	if ( !data )
564 	{
565 		EOSFuncs::logError("OnLobbyLeaveCallback: null data");
566 	}
567 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
568 	{
569 		EOSFuncs::logInfo("OnLobbyLeaveCallback: Left lobby id: %s", data->LobbyId);
570 		if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
571 		{
572 			LobbyLeaveCleanup(EOS.CurrentLobbyData);
573 		}
574 		return;
575 	}
576 	else if ( data->ResultCode == EOS_EResult::EOS_NotFound )
577 	{
578 		EOSFuncs::logInfo("OnLobbyLeaveCallback: Could not find lobby id to leave: %s", data->LobbyId);
579 		if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
580 		{
581 			LobbyLeaveCleanup(EOS.CurrentLobbyData);
582 		}
583 		return;
584 	}
585 	else
586 	{
587 		EOSFuncs::logError("OnLobbyLeaveCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
588 	}
589 }
590 
OnIncomingConnectionRequest(const EOS_P2P_OnIncomingConnectionRequestInfo * data)591 void EOS_CALL EOSFuncs::OnIncomingConnectionRequest(const EOS_P2P_OnIncomingConnectionRequestInfo* data)
592 {
593 	if ( data )
594 	{
595 		std::string SocketName = data->SocketId->SocketName;
596 		if ( SocketName != "CHAT" )
597 		{
598 			EOSFuncs::logError("OnIncomingConnectionRequest: bad socket id: %s", SocketName.c_str());
599 			return;
600 		}
601 
602 		EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(EOS.PlatformHandle);
603 		EOS_P2P_AcceptConnectionOptions Options;
604 		Options.ApiVersion = EOS_P2P_ACCEPTCONNECTION_API_LATEST;
605 		Options.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
606 		Options.RemoteUserId = data->RemoteUserId;
607 
608 		EOS_P2P_SocketId SocketId;
609 		SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
610 		strncpy(SocketId.SocketName, "CHAT", 5);
611 		Options.SocketId = &SocketId;
612 
613 		EOS_EResult Result = EOS_P2P_AcceptConnection(P2PHandle, &Options);
614 		if ( Result != EOS_EResult::EOS_Success )
615 		{
616 			EOSFuncs::logError("OnIncomingConnectionRequest: error while accepting connection, code: %d", static_cast<int>(Result));
617 		}
618 		else
619 		{
620 			EOS.P2PConnectionInfo.insertProductIdIntoPeers(Options.RemoteUserId);
621 		}
622 	}
623 	else
624 	{
625 		EOSFuncs::logError("OnIncomingConnectionRequest: null data");
626 	}
627 }
628 
OnLobbyUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo * data)629 void EOS_CALL EOSFuncs::OnLobbyUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo* data)
630 {
631 	if ( data )
632 	{
633 		if ( EOS.LobbyModificationHandle )
634 		{
635 			EOS_LobbyModification_Release(EOS.LobbyModificationHandle);
636 			EOS.LobbyModificationHandle = nullptr;
637 		}
638 
639 		if ( data->ResultCode != EOS_EResult::EOS_Success )
640 		{
641 			EOSFuncs::logError("OnLobbyUpdateFinished: Callback failure: %d", static_cast<int>(data->ResultCode));
642 			return;
643 		}
644 		else
645 		{
646 			EOSFuncs::logInfo("OnLobbyUpdateFinished: Success");
647 		}
648 	}
649 	else
650 	{
651 		EOSFuncs::logError("OnLobbyUpdateFinished: null data");
652 	}
653 }
654 
OnLobbyMemberUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo * data)655 void EOS_CALL EOSFuncs::OnLobbyMemberUpdateFinished(const EOS_Lobby_UpdateLobbyCallbackInfo* data)
656 {
657 	if ( data )
658 	{
659 		if ( EOS.LobbyMemberModificationHandle )
660 		{
661 			EOS_LobbyModification_Release(EOS.LobbyMemberModificationHandle);
662 			EOS.LobbyMemberModificationHandle = nullptr;
663 		}
664 
665 		if ( data->ResultCode != EOS_EResult::EOS_Success && data->ResultCode != EOS_EResult::EOS_NoChange )
666 		{
667 			EOSFuncs::logError("OnLobbyMemberUpdateFinished: Callback failure: %d", static_cast<int>(data->ResultCode));
668 			return;
669 		}
670 		else
671 		{
672 			EOSFuncs::logInfo("OnLobbyMemberUpdateFinished: Success");
673 		}
674 	}
675 	else
676 	{
677 		EOSFuncs::logError("OnLobbyMemberUpdateFinished: null data");
678 	}
679 }
680 
OnQueryAccountMappingsCallback(const EOS_Connect_QueryProductUserIdMappingsCallbackInfo * data)681 void EOS_CALL EOSFuncs::OnQueryAccountMappingsCallback(const EOS_Connect_QueryProductUserIdMappingsCallbackInfo* data)
682 {
683 	if ( data )
684 	{
685 		if ( data->ResultCode == EOS_EResult::EOS_Success )
686 		{
687 			EOSFuncs::logInfo("OnQueryAccountMappingsCallback: Success");
688 
689 			bool authEnabledForLocalUser = false; // if we're using auth() interface we can use EpicAccountIds for queries
690 			EOS_ELoginStatus loginStatus =  EOS_Auth_GetLoginStatus(EOS_Platform_GetAuthInterface(EOS.PlatformHandle),
691 				EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str()));
692 			if ( loginStatus == EOS_ELoginStatus::EOS_LS_LoggedIn )
693 			{
694 				authEnabledForLocalUser = true;
695 			}
696 
697 			std::vector<EOS_ProductUserId> MappingsReceived;
698 			for ( const EOS_ProductUserId& productId : EOS.ProductIdsAwaitingAccountMappingCallback )
699 			{
700 				EOS_Connect_GetProductUserIdMappingOptions Options = {};
701 				Options.ApiVersion = EOS_CONNECT_GETPRODUCTUSERIDMAPPING_API_LATEST;
702 				Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_EPIC;
703 				Options.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
704 				Options.TargetProductUserId = productId;
705 
706 				EOS_HConnect ConnectHandle = EOS_Platform_GetConnectInterface(EOS.PlatformHandle);
707 				char buffer[EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH];
708 				int bufferSize = EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH;
709 				EOS_EResult Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize);
710 				if ( Result == EOS_EResult::EOS_Success )
711 				{
712 					std::string receivedStr(buffer, bufferSize);
713 					EOS_EpicAccountId epicAccountId = EOSFuncs::Helpers_t::epicIdFromString(receivedStr.c_str());
714 
715 					// insert the ids into the global map
716 					if ( authEnabledForLocalUser )
717 					{
718 						EOS.AccountMappings.insert(std::pair<EOS_ProductUserId, EOS_EpicAccountId>(productId,epicAccountId));
719 					}
720 					else
721 					{
722 						EOS.ExternalAccountMappings.insert(std::pair<EOS_ProductUserId, std::string>(productId, buffer));
723 					}
724 
725 					for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby )
726 					{
727 						if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) )
728 						{
729 							player.memberEpicAccountId = receivedStr;
730 							if ( authEnabledForLocalUser )
731 							{
732 								EOS.getUserInfo(epicAccountId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER, 0);
733 								EOSFuncs::logInfo("OnQueryAccountMappingsCallback: product id: %s, epic account id: %s",
734 									player.memberProductUserId.c_str(), player.memberEpicAccountId.c_str());
735 							}
736 							else
737 							{
738 								EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER);
739 							}
740 							break;
741 						}
742 					}
743 					MappingsReceived.push_back(productId);
744 				}
745 				else if ( Result == EOS_EResult::EOS_NotFound )
746 				{
747 					// try different account types
748 					Options.AccountIdType = EOS_EExternalAccountType::EOS_EAT_STEAM;
749 					Result = EOS_Connect_GetProductUserIdMapping(ConnectHandle, &Options, buffer, &bufferSize);
750 					if ( Result == EOS_EResult::EOS_Success )
751 					{
752 						EOS.ExternalAccountMappings.insert(std::pair<EOS_ProductUserId, std::string>(productId, buffer));
753 						if ( EOSFuncs::Helpers_t::isMatchingProductIds(EOS.CurrentUserInfo.getProductUserIdHandle(), productId) )
754 						{
755 							EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOCAL);
756 						}
757 						for ( LobbyData_t::PlayerLobbyData_t& player : EOS.CurrentLobbyData.playersInLobby )
758 						{
759 							if ( EOSFuncs::Helpers_t::isMatchingProductIds(productId, EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str())) )
760 							{
761 								EOS.getExternalAccountUserInfo(productId, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER);
762 							}
763 						}
764 						MappingsReceived.push_back(productId);
765 					}
766 				}
767 			}
768 
769 			for ( const EOS_ProductUserId& productId : MappingsReceived )
770 			{
771 				EOS.ProductIdsAwaitingAccountMappingCallback.erase(productId);
772 			}
773 		}
774 		else if ( data->ResultCode != EOS_EResult::EOS_OperationWillRetry )
775 		{
776 			EOSFuncs::logError("OnQueryAccountMappingsCallback: retrying");
777 		}
778 	}
779 }
780 
getExternalAccountUserInfo(EOS_ProductUserId targetId,UserInfoQueryType queryType)781 void EOSFuncs::getExternalAccountUserInfo(EOS_ProductUserId targetId, UserInfoQueryType queryType)
782 {
783 	EOS_Connect_CopyProductUserInfoOptions CopyProductUserInfoOptions = {};
784 	CopyProductUserInfoOptions.ApiVersion = EOS_CONNECT_COPYPRODUCTUSERINFO_API_LATEST;
785 	CopyProductUserInfoOptions.TargetUserId = targetId;
786 
787 	EOS_Connect_ExternalAccountInfo* ExternalAccountInfo = nullptr;
788 	EOS_EResult CopyResult = EOS_Connect_CopyProductUserInfo(ConnectHandle, &CopyProductUserInfoOptions, &ExternalAccountInfo);
789 	if ( CopyResult != EOS_EResult::EOS_Success )
790 	{
791 		EOSFuncs::logInfo("getExternalAccountUserInfo: Error %d", static_cast<int>(CopyResult));
792 		EOS_Connect_ExternalAccountInfo_Release(ExternalAccountInfo);
793 		return;
794 	}
795 	else if ( !ExternalAccountInfo )
796 	{
797 		EOSFuncs::logInfo("getExternalAccountUserInfo: Info was null");
798 		return;
799 	}
800 
801 	EOSFuncs::logInfo("getExternalAccountUserInfo: Received info for product id: %s", EOSFuncs::Helpers_t::shortProductIdToString(targetId).c_str());
802 
803 	if ( queryType == UserInfoQueryType::USER_INFO_QUERY_LOCAL )
804 	{
805 		// local user
806 		EOS.CurrentUserInfo.Name = ExternalAccountInfo->DisplayName;
807 		EOS.CurrentUserInfo.bUserInfoRequireUpdate = false;
808 		EOSFuncs::logInfo("getExternalAccountUserInfo: Current User Name: %s", ExternalAccountInfo->DisplayName);
809 	}
810 	else if ( queryType == UserInfoQueryType::USER_INFO_QUERY_LOBBY_MEMBER )
811 	{
812 		if ( !CurrentLobbyData.currentLobbyIsValid() || CurrentLobbyData.playersInLobby.empty() )
813 		{
814 			EOSFuncs::logInfo("getExternalAccountUserInfo: lobby member request failed due to invalid or no player data in lobby");
815 		}
816 		else
817 		{
818 			bool foundMember = false;
819 			std::string queryTarget = EOSFuncs::Helpers_t::productIdToString(targetId);
820 			for ( auto& it : EOS.CurrentLobbyData.playersInLobby )
821 			{
822 				if ( queryTarget.compare(it.memberProductUserId.c_str()) == 0 )
823 				{
824 					foundMember = true;
825 					it.name = ExternalAccountInfo->DisplayName;
826 					it.accountType = ExternalAccountInfo->AccountIdType;
827 					it.bUserInfoRequireUpdate = false;
828 					EOSFuncs::logInfo("getExternalAccountUserInfo: found lobby username: %s, account type: %d", ExternalAccountInfo->DisplayName, ExternalAccountInfo->AccountIdType);
829 					break;
830 				}
831 			}
832 			if ( !foundMember )
833 			{
834 				EOSFuncs::logInfo("getExternalAccountUserInfo: could not find player in current lobby with product id %s",
835 					EOSFuncs::Helpers_t::shortProductIdToString(targetId).c_str());
836 			}
837 		}
838 	}
839 	EOS_Connect_ExternalAccountInfo_Release(ExternalAccountInfo);
840 }
841 
OnMemberUpdateReceived(const EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo * data)842 void EOS_CALL EOSFuncs::OnMemberUpdateReceived(const EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo* data)
843 {
844 	if ( data )
845 	{
846 		if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
847 		{
848 			EOS.CurrentLobbyData.updateLobby();
849 			EOSFuncs::logInfo("OnMemberUpdateReceived: received user: %s, updating lobby", EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str());
850 		}
851 		else
852 		{
853 			EOSFuncs::logInfo("OnMemberUpdateReceived: success. lobby id was not CurrentLobbyData");
854 		}
855 	}
856 	else
857 	{
858 		EOSFuncs::logError("OnMemberUpdateReceived: null data");
859 	}
860 }
861 
OnMemberStatusReceived(const EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo * data)862 void EOS_CALL EOSFuncs::OnMemberStatusReceived(const EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo* data)
863 {
864 	if ( data )
865 	{
866 		switch ( data->CurrentStatus )
867 		{
868 			case EOS_ELobbyMemberStatus::EOS_LMS_CLOSED:
869 			case EOS_ELobbyMemberStatus::EOS_LMS_DISCONNECTED:
870 			case EOS_ELobbyMemberStatus::EOS_LMS_KICKED:
871 			case EOS_ELobbyMemberStatus::EOS_LMS_LEFT:
872 				if ( EOS.P2PConnectionInfo.isPeerIndexed(data->TargetUserId) )
873 				{
874 					EOS_P2P_CloseConnectionOptions closeOptions = {};
875 					closeOptions.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST;
876 					closeOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
877 					closeOptions.RemoteUserId = data->TargetUserId;
878 
879 					EOS_P2P_SocketId SocketId = {};
880 					SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
881 					strncpy(SocketId.SocketName, "CHAT", 5);
882 					closeOptions.SocketId = &SocketId;
883 					EOS_EResult result = EOS_P2P_CloseConnection(EOS_Platform_GetP2PInterface(EOS.PlatformHandle), &closeOptions);
884 					EOSFuncs::logInfo("OnMemberStatusReceived: closing P2P connections, result: %d", static_cast<int>(result));
885 
886 					EOS.P2PConnectionInfo.assignPeerIndex(data->TargetUserId, -1);
887 				}
888 
889 				if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
890 				{
891 					if ( data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_CLOSED
892 						|| (data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_KICKED
893 							&& (data->TargetUserId == EOS.CurrentUserInfo.getProductUserIdHandle()))
894 						)
895 					{
896 						// if lobby closed or we got kicked, then clear data.
897 						LobbyLeaveCleanup(EOS.CurrentLobbyData);
898 					}
899 					else
900 					{
901 						EOS.CurrentLobbyData.updateLobby();
902 						EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby",
903 							EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(),
904 							static_cast<int>(data->CurrentStatus));
905 						return;
906 					}
907 				}
908 				break;
909 			case EOS_ELobbyMemberStatus::EOS_LMS_JOINED:
910 			case EOS_ELobbyMemberStatus::EOS_LMS_PROMOTED:
911 				if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
912 				{
913 					EOS.CurrentLobbyData.updateLobby();
914 					EOSFuncs::logInfo("OnMemberStatusReceived: received user: %s, event: %d, updating lobby",
915 						EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(),
916 						static_cast<int>(data->CurrentStatus));
917 					return;
918 				}
919 				break;
920 			default:
921 				break;
922 		}
923 		EOSFuncs::logInfo("OnMemberStatusReceived: success, received user: %s | status: %d",
924 			EOSFuncs::Helpers_t::shortProductIdToString(data->TargetUserId).c_str(), static_cast<int>(data->CurrentStatus));
925 	}
926 	else
927 	{
928 		EOSFuncs::logError("OnMemberStatusReceived: null data");
929 	}
930 
931 	//	bool UpdateLobby = true;
932 	//	//Current player updates need special handling
933 	//	if ( Data->TargetUserId == Player->GetProductUserID() )
934 	//	{
935 	//		if ( Data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_CLOSED ||
936 	//			Data->CurrentStatus == EOS_ELobbyMemberStatus::EOS_LMS_KICKED )
937 	//		{
938 	//			FGame::Get().GetLobbies()->OnKickedFromLobby(Data->LobbyId);
939 	//			UpdateLobby = false;
940 	//		}
941 	//	}
942 
943 	//	if ( UpdateLobby )
944 	//	{
945 	//		//Simply update the whole lobby
946 	//		FGame::Get().GetLobbies()->OnLobbyUpdate(Data->LobbyId);
947 	//	}
948 }
949 
OnLobbyUpdateReceived(const EOS_Lobby_LobbyUpdateReceivedCallbackInfo * data)950 void EOS_CALL EOSFuncs::OnLobbyUpdateReceived(const EOS_Lobby_LobbyUpdateReceivedCallbackInfo* data)
951 {
952 	if ( data )
953 	{
954 		if ( EOS.CurrentLobbyData.LobbyId.compare(data->LobbyId) == 0 )
955 		{
956 			EOS.CurrentLobbyData.updateLobby();
957 		}
958 		EOSFuncs::logInfo("OnLobbyUpdateReceived: success");
959 	}
960 	else
961 	{
962 		EOSFuncs::logError("OnLobbyUpdateReceived: null data");
963 	}
964 }
965 
OnDestroyLobbyFinished(const EOS_Lobby_DestroyLobbyCallbackInfo * data)966 void EOS_CALL EOSFuncs::OnDestroyLobbyFinished(const EOS_Lobby_DestroyLobbyCallbackInfo* data)
967 {
968 	if ( data )
969 	{
970 		if ( data->ResultCode != EOS_EResult::EOS_Success )
971 		{
972 			EOSFuncs::logError("OnDestroyLobbyFinished: error destroying lobby id: %s code: %d", data->LobbyId, static_cast<int>(data->ResultCode));
973 		}
974 		else
975 		{
976 			EOSFuncs::logInfo("OnDestroyLobbyFinished: success, id %s", data->LobbyId);
977 		}
978 	}
979 	else
980 	{
981 		EOSFuncs::logError("OnDestroyLobbyFinished: null data");
982 	}
983 }
984 
ConnectAuthExpirationCallback(const EOS_Connect_AuthExpirationCallbackInfo * data)985 void EOS_CALL EOSFuncs::ConnectAuthExpirationCallback(const EOS_Connect_AuthExpirationCallbackInfo* data)
986 {
987 	if ( data )
988 	{
989 		EOSFuncs::logInfo("ConnectAuthExpirationCallback: connect auth expiring - product id: %s",
990 			EOSFuncs::Helpers_t::productIdToString(data->LocalUserId));
991 #ifdef STEAMWORKS
992 		if ( LobbyHandler.crossplayEnabled )
993 		{
994 			EOSFuncs::logInfo("ConnectAuthExpirationCallback: Reconnecting crossplay account");
995 			EOS.CrossplayAccountManager.autologin = true;
996 			EOS.CrossplayAccountManager.connectLoginCompleted = EOS_EResult::EOS_NotConfigured;
997 		}
998 #else
999 		EOS.initConnectLogin();
1000 #endif // STEAMWORKS
1001 	}
1002 	else
1003 	{
1004 		EOSFuncs::logError("ConnectAuthExpirationCallback: null data");
1005 	}
1006 }
1007 
serialize(void * file)1008 void EOSFuncs::serialize(void* file) {
1009 	int version = 0;
1010 	FileInterface* fileInterface = static_cast<FileInterface*>(file);
1011 	fileInterface->property("version", version);
1012 	fileInterface->property("credentialhost", CredentialHost);
1013 	fileInterface->property("credentialname", CredentialName);
1014 }
1015 
initPlatform(bool enableLogging)1016 bool EOSFuncs::initPlatform(bool enableLogging)
1017 {
1018 	EOS_InitializeOptions InitializeOptions;
1019 	InitializeOptions.ProductName = "Barony";
1020 	InitializeOptions.ProductVersion = VERSION;
1021 	InitializeOptions.ApiVersion = EOS_INITIALIZE_API_LATEST;
1022 	InitializeOptions.AllocateMemoryFunction = nullptr;
1023 	InitializeOptions.ReallocateMemoryFunction = nullptr;
1024 	InitializeOptions.ReleaseMemoryFunction = nullptr;
1025 	InitializeOptions.Reserved = nullptr;
1026 	InitializeOptions.SystemInitializeOptions = nullptr;
1027 	EOS_EResult result = EOS_Initialize(&InitializeOptions);
1028 	if ( result != EOS_EResult::EOS_Success )
1029 	{
1030 		logError("initPlatform: Failure to initialize - error code: %d", static_cast<int>(result));
1031 		return false;
1032 	}
1033 	else
1034 	{
1035 		logInfo("initPlatform: Initialize success");
1036 	}
1037 
1038 	if ( enableLogging )
1039 	{
1040 		EOS_EResult SetLogCallbackResult = EOS_Logging_SetCallback(&this->LoggingCallback);
1041 		if ( SetLogCallbackResult != EOS_EResult::EOS_Success )
1042 		{
1043 			logError("SetLogCallbackResult: Set Logging Callback Failed!");
1044 		}
1045 		else
1046 		{
1047 			logInfo("SetLogCallbackResult: Logging Callback set");
1048 			EOS_Logging_SetLogLevel(EOS_ELogCategory::EOS_LC_ALL_CATEGORIES, EOS_ELogLevel::EOS_LOG_Warning);
1049 		}
1050 	}
1051 
1052 	EOS_Platform_Options PlatformOptions = {};
1053 	PlatformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST;
1054 	PlatformOptions.Reserved = nullptr;
1055 	PlatformOptions.ProductId = BUILD_ENV_PR;
1056 	PlatformOptions.SandboxId = BUILD_ENV_SA;
1057 	PlatformOptions.DeploymentId = BUILD_ENV_DE;
1058 	PlatformOptions.ClientCredentials.ClientId = BUILD_ENV_CC;
1059 	PlatformOptions.ClientCredentials.ClientSecret = BUILD_ENV_CS;
1060 	PlatformOptions.OverrideCountryCode = nullptr;
1061 	PlatformOptions.OverrideLocaleCode = nullptr;
1062 	PlatformOptions.bIsServer = EOS_FALSE;
1063 	PlatformOptions.Flags = EOS_PF_DISABLE_OVERLAY;
1064 	static std::string EncryptionKey(64, '1');
1065 	PlatformOptions.EncryptionKey = EncryptionKey.c_str();
1066 	PlatformOptions.CacheDirectory = nullptr; // important - needs double slashes and absolute path
1067 
1068 	PlatformHandle = EOS_Platform_Create(&PlatformOptions);
1069 	PlatformOptions.bIsServer = EOS_TRUE;
1070 	ServerPlatformHandle = EOS_Platform_Create(&PlatformOptions);
1071 
1072 	PlatformOptions.ClientCredentials.ClientId = nullptr;
1073 	PlatformOptions.ClientCredentials.ClientSecret = nullptr;
1074 	PlatformOptions.ProductId = nullptr;
1075 	PlatformOptions.SandboxId = nullptr;
1076 	PlatformOptions.DeploymentId = nullptr;
1077 
1078 	if ( !PlatformHandle )
1079 	{
1080 		logError("PlatformHandle: Platform failed to initialize - invalid handle");
1081 		return false;
1082 	}
1083 #ifndef STEAMWORKS
1084 #ifdef WINDOWS
1085 #ifdef NDEBUG
1086 	appRequiresRestart = EOS_Platform_CheckForLauncherAndRestart(EOS.PlatformHandle);
1087 #endif
1088 #else
1089 	// #ifdef APPLE
1090 	// 	SDL_Event event;
1091 	// 	Uint32 startAuthTicks = SDL_GetTicks();
1092 	// 	Uint32 currentAuthTicks = startAuthTicks;
1093 	// 	while (1)
1094 	// 	{
1095 	// 		while ( SDL_PollEvent(&event) != 0 )
1096 	// 		{
1097 	// 			//Makes Mac work because Apple had to do it different.
1098 	// 		}
1099 	// 		EOS_Platform_Tick(PlatformHandle);
1100 	// 		SDL_Delay(50);
1101 
1102 	// 		currentAuthTicks = SDL_GetTicks();
1103 	// 		logInfo("*whirl*");
1104 	// 		if ( currentAuthTicks - startAuthTicks >= 5000 ) // spin the wheels for 5 seconds
1105 	// 		{
1106 	// 			break;
1107 	// 		}
1108 	// 	}
1109 	// #endif
1110 	appRequiresRestart = EOS_Platform_CheckForLauncherAndRestart(EOS.PlatformHandle);
1111 	// printlog("See you on the other side!");
1112 	// while ( SDL_PollEvent(&event) != 0 )
1113 	// {
1114 	// 	//Makes Mac work because Apple had to do it different.
1115 	// }
1116 	// EOS_Platform_Tick(PlatformHandle);
1117 	// while ( SDL_PollEvent(&event) != 0 )
1118 	// {
1119 	// 	//Makes Mac work because Apple had to do it different.
1120 	// }
1121 	// exit(1);
1122 #endif
1123 #endif
1124 
1125 #ifdef STEAMWORKS
1126 	EOS.StatGlobalManager.queryGlobalStatUser();
1127 #endif
1128 	return true;
1129 }
1130 
initConnectLogin()1131 void EOSFuncs::initConnectLogin() // should not handle for Steam connect logins
1132 {
1133 	ConnectHandle = EOS_Platform_GetConnectInterface(PlatformHandle);
1134 
1135 	EOS_Auth_Token* UserAuthToken = nullptr;
1136 	EOS_Auth_CopyUserAuthTokenOptions CopyTokenOptions = { 0 };
1137 	CopyTokenOptions.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST;
1138 
1139 	if ( EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions,
1140 		EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str()), &UserAuthToken) == EOS_EResult::EOS_Success )
1141 	{
1142 		logInfo("initConnectLogin: Auth expires: %f", UserAuthToken->ExpiresIn);
1143 
1144 		EOS_Connect_Credentials Credentials;
1145 		Credentials.ApiVersion = EOS_CONNECT_CREDENTIALS_API_LATEST;
1146 		Credentials.Token = UserAuthToken->AccessToken;
1147 		Credentials.Type = EOS_EExternalCredentialType::EOS_ECT_EPIC; // change this to steam etc for different account providers.
1148 
1149 		EOS_Connect_LoginOptions Options;
1150 		Options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST;
1151 		Options.Credentials = &Credentials;
1152 		Options.UserLoginInfo = nullptr;
1153 
1154 		EOS_Connect_Login(ConnectHandle, &Options, nullptr, ConnectLoginCompleteCallback);
1155 		EOS_Auth_Token_Release(UserAuthToken);
1156 	}
1157 }
1158 
readFromFile()1159 void EOSFuncs::readFromFile()
1160 {
1161 	if ( PHYSFS_getRealDir("/data/eos.json") )
1162 	{
1163 		std::string inputPath = PHYSFS_getRealDir("/data/eos.json");
1164 		inputPath.append("/data/eos.json");
1165 		if ( FileHelper::readObject(inputPath.c_str(), *this) )
1166 		{
1167 			EOSFuncs::logInfo("[JSON]: Successfully read json file %s", inputPath.c_str());
1168 		}
1169 	}
1170 }
1171 
readFromCmdLineArgs()1172 void EOSFuncs::readFromCmdLineArgs()
1173 {
1174 	for ( auto& arg : CommandLineArgs )
1175 	{
1176 		if ( arg.find("-AUTH_PASSWORD=") != std::string::npos )
1177 		{
1178 			EOSFuncs::logInfo("Launching from store...");
1179 			CredentialName = arg.substr(strlen("-AUTH_PASSWORD="));
1180 		}
1181 		else if ( arg.find("-AUTH_TYPE=exchangecode") != std::string::npos )
1182 		{
1183 			EOS.AccountManager.AuthType = EOS_ELoginCredentialType::EOS_LCT_ExchangeCode;
1184 		}
1185 	}
1186 }
1187 
HandleReceivedMessages(EOS_ProductUserId * remoteIdReturn)1188 bool EOSFuncs::HandleReceivedMessages(EOS_ProductUserId* remoteIdReturn)
1189 {
1190 	if ( !CurrentUserInfo.isValid() )
1191 	{
1192 		//logError("HandleReceivedMessages: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr());
1193 		return false;
1194 	}
1195 
1196 	if ( !net_packet )
1197 	{
1198 		return false;
1199 	}
1200 
1201 	EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(PlatformHandle);
1202 
1203 	EOS_P2P_ReceivePacketOptions ReceivePacketOptions;
1204 	ReceivePacketOptions.ApiVersion = EOS_P2P_RECEIVEPACKET_API_LATEST;
1205 	ReceivePacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1206 	ReceivePacketOptions.MaxDataSizeBytes = 512;
1207 	ReceivePacketOptions.RequestedChannel = nullptr;
1208 
1209 	EOS_P2P_SocketId SocketId;
1210 	SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
1211 	uint8_t Channel = 0;
1212 
1213 	Uint32 bytesWritten = 0;
1214 	EOS_EResult result = EOS_P2P_ReceivePacket(P2PHandle, &ReceivePacketOptions, remoteIdReturn, &SocketId, &Channel, net_packet->data, &bytesWritten);
1215 	if ( result == EOS_EResult::EOS_NotFound
1216 		|| result == EOS_EResult::EOS_InvalidAuth
1217 		|| result == EOS_EResult::EOS_InvalidUser )
1218 	{
1219 		//no more packets, just end
1220 		return false;
1221 	}
1222 	else if ( result == EOS_EResult::EOS_Success )
1223 	{
1224 		net_packet->len = bytesWritten;
1225 		//char buffer[512] = "";
1226 		//strncpy_s(buffer, (char*)net_packet->data, 512 - 1);
1227 		//buffer[4] = '\0';
1228 		//logInfo("HandleReceivedMessages: remote id: %s received: %s", EOSFuncs::Helpers_t::productIdToString(*remoteIdReturn).c_str(), buffer);
1229 		return true;
1230 	}
1231 	else
1232 	{
1233 		logError("HandleReceivedMessages: error while reading data, code: %d", static_cast<int>(result));
1234 		return false;
1235 	}
1236 }
1237 
1238 // function to empty the packet queue on main lobby.
HandleReceivedMessagesAndIgnore(EOS_ProductUserId * remoteIdReturn)1239 bool EOSFuncs::HandleReceivedMessagesAndIgnore(EOS_ProductUserId* remoteIdReturn)
1240 {
1241 	if ( !CurrentUserInfo.isValid() )
1242 	{
1243 		//logError("HandleReceivedMessages: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr());
1244 		return false;
1245 	}
1246 	EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(PlatformHandle);
1247 
1248 	EOS_P2P_ReceivePacketOptions ReceivePacketOptions;
1249 	ReceivePacketOptions.ApiVersion = EOS_P2P_RECEIVEPACKET_API_LATEST;
1250 	ReceivePacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1251 	ReceivePacketOptions.MaxDataSizeBytes = 512;
1252 	ReceivePacketOptions.RequestedChannel = nullptr;
1253 
1254 	EOS_P2P_SocketId SocketId;
1255 	SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
1256 	uint8_t Channel = 0;
1257 
1258 	Uint32 bytesWritten = 0;
1259 	Uint8 dummyData[512];
1260 	EOS_EResult result = EOS_P2P_ReceivePacket(P2PHandle, &ReceivePacketOptions, remoteIdReturn, &SocketId, &Channel, dummyData, &bytesWritten);
1261 	if ( result == EOS_EResult::EOS_NotFound
1262 		|| result == EOS_EResult::EOS_InvalidAuth
1263 		|| result == EOS_EResult::EOS_InvalidUser )
1264 	{
1265 		//no more packets, just end
1266 		return false;
1267 	}
1268 	else if ( result == EOS_EResult::EOS_Success )
1269 	{
1270 		char buffer[512] = "";
1271 		strncpy(buffer, (char*)dummyData, 512 - 1);
1272 		buffer[4] = '\0';
1273 		std::string remoteStr = EOSFuncs::Helpers_t::productIdToString(*remoteIdReturn);
1274 		if ( (int)buffer[3] < '0'
1275 			&& (int)buffer[0] == 0
1276 			&& (int)buffer[1] == 0
1277 			&& (int)buffer[2] == 0 )
1278 		{
1279 			logInfo("Clearing P2P packet queue: remote id: %s received: %d", remoteStr.c_str(), (int)buffer[3]);
1280 		}
1281 		else
1282 		{
1283 			logInfo("Clearing P2P packet queue: remote id: %s received: %s", remoteStr.c_str(), buffer);
1284 		}
1285 		return true;
1286 	}
1287 	else
1288 	{
1289 		logError("HandleReceivedMessagesAndIgnore: error while reading data, code: %d", static_cast<int>(result));
1290 		return false;
1291 	}
1292 }
1293 
SendMessageP2P(EOS_ProductUserId RemoteId,const void * data,int len)1294 void EOSFuncs::SendMessageP2P(EOS_ProductUserId RemoteId, const void* data, int len)
1295 {
1296 	if ( !EOSFuncs::Helpers_t::productIdIsValid(RemoteId) )
1297 	{
1298 		logError("SendMessageP2P: Invalid remote Id: %s", EOSFuncs::Helpers_t::productIdToString(RemoteId));
1299 		return;
1300 	}
1301 
1302 	if ( !CurrentUserInfo.isValid() )
1303 	{
1304 		logError("SendMessageP2P: Invalid local user Id: %s", CurrentUserInfo.getProductUserIdStr());
1305 		return;
1306 	}
1307 
1308 	EOS_HP2P P2PHandle = EOS_Platform_GetP2PInterface(PlatformHandle);
1309 
1310 	EOS_P2P_SocketId SocketId;
1311 	SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
1312 	strncpy(SocketId.SocketName, "CHAT", 5);
1313 
1314 	EOS_P2P_SendPacketOptions SendPacketOptions = {};
1315 	SendPacketOptions.ApiVersion = EOS_P2P_SENDPACKET_API_LATEST;
1316 	SendPacketOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1317 	SendPacketOptions.RemoteUserId = RemoteId;
1318 	SendPacketOptions.SocketId = &SocketId;
1319 	SendPacketOptions.bAllowDelayedDelivery = EOS_TRUE;
1320 	SendPacketOptions.Channel = 0;
1321 	SendPacketOptions.Reliability = EOS_EPacketReliability::EOS_PR_UnreliableUnordered;
1322 
1323 	SendPacketOptions.DataLengthBytes = len;
1324 	SendPacketOptions.Data = (char*)data;
1325 
1326 	EOS_EResult Result = EOS_P2P_SendPacket(P2PHandle, &SendPacketOptions);
1327 	if ( Result != EOS_EResult::EOS_Success )
1328 	{
1329 		logError("SendMessageP2P: error while sending data, code: %d", static_cast<int>(Result));
1330 	}
1331 }
1332 
setLobbyAttributesFromGame(HostUpdateLobbyTypes updateType)1333 void EOSFuncs::LobbyData_t::setLobbyAttributesFromGame(HostUpdateLobbyTypes updateType)
1334 {
1335 	if ( updateType == LOBBY_UPDATE_MAIN_MENU )
1336 	{
1337 		LobbyAttributes.lobbyName = EOS.currentLobbyName;
1338 		LobbyAttributes.gameVersion = VERSION;
1339 		LobbyAttributes.isLobbyLoadingSavedGame = loadingsavegame;
1340 		LobbyAttributes.serverFlags = svFlags;
1341 		LobbyAttributes.numServerMods = 0;
1342 		LobbyAttributes.PermissionLevel = static_cast<Uint32>(EOS.currentPermissionLevel);
1343 		LobbyAttributes.maxplayersCompatible = MAXPLAYERS;
1344 	}
1345 	else if ( updateType == LOBBY_UPDATE_DURING_GAME )
1346 	{
1347 		LobbyAttributes.serverFlags = svFlags;
1348 		LobbyAttributes.gameCurrentLevel = currentlevel;
1349 	}
1350 }
1351 
setBasicCurrentLobbyDataFromInitialJoin(LobbyData_t * lobbyToJoin)1352 void EOSFuncs::LobbyData_t::setBasicCurrentLobbyDataFromInitialJoin(LobbyData_t* lobbyToJoin)
1353 {
1354 	if ( !lobbyToJoin )
1355 	{
1356 		logError("setBasicCurrentLobbyDataFromInitialJoin: invalid lobby passed.");
1357 		return;
1358 	}
1359 
1360 	MaxPlayers = lobbyToJoin->MaxPlayers;
1361 	OwnerProductUserId = lobbyToJoin->OwnerProductUserId;
1362 	LobbyId = lobbyToJoin->LobbyId;
1363 	FreeSlots = lobbyToJoin->FreeSlots;
1364 	bLobbyHasBasicDetailsRead = true;
1365 
1366 	EOS.P2PConnectionInfo.serverProductId = EOSFuncs::Helpers_t::productIdFromString(OwnerProductUserId.c_str());
1367 
1368 	EOS.P2PConnectionInfo.peerProductIds.clear();
1369 	for ( PlayerLobbyData_t& player : playersInLobby )
1370 	{
1371 		EOS_ProductUserId productId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str());
1372 		EOS.P2PConnectionInfo.insertProductIdIntoPeers(productId);
1373 	}
1374 }
1375 
currentUserIsOwner()1376 bool EOSFuncs::LobbyData_t::currentUserIsOwner()
1377 {
1378 	if ( currentLobbyIsValid() && OwnerProductUserId.compare(EOS.CurrentUserInfo.getProductUserIdStr()) == 0 )
1379 	{
1380 		return true;
1381 	}
1382 	return false;
1383 };
1384 
updateLobbyForHost(HostUpdateLobbyTypes updateType)1385 bool EOSFuncs::LobbyData_t::updateLobbyForHost(HostUpdateLobbyTypes updateType)
1386 {
1387 	if ( !EOSFuncs::Helpers_t::isMatchingProductIds(EOSFuncs::Helpers_t::productIdFromString(this->OwnerProductUserId.c_str()),
1388 		EOS.CurrentUserInfo.getProductUserIdHandle()) )
1389 	{
1390 		EOSFuncs::logError("updateLobby: current user is not lobby owner");
1391 		return false;
1392 	}
1393 
1394 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1395 	EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions = {};
1396 	ModifyOptions.ApiVersion = EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST;
1397 	ModifyOptions.LobbyId = this->LobbyId.c_str();
1398 	ModifyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
1399 
1400 	if ( EOS.LobbyModificationHandle )
1401 	{
1402 		EOS_LobbyModification_Release(EOS.LobbyModificationHandle);
1403 		EOS.LobbyModificationHandle = nullptr;
1404 	}
1405 	EOS_HLobbyModification LobbyModification = nullptr;
1406 	EOS_EResult result = EOS_Lobby_UpdateLobbyModification(LobbyHandle, &ModifyOptions, &LobbyModification);
1407 	if ( result != EOS_EResult::EOS_Success )
1408 	{
1409 		EOSFuncs::logError("updateLobby: Could not create lobby modification. Error code: %d", static_cast<int>(result));
1410 		return false;
1411 	}
1412 
1413 	EOS.LobbyModificationHandle = LobbyModification;
1414 	setLobbyAttributesFromGame(updateType);
1415 
1416 	/*EOS_LobbyModification_SetPermissionLevelOptions permissionOptions = {};
1417 	permissionOptions.ApiVersion = EOS_LOBBYMODIFICATION_SETPERMISSIONLEVEL_API_LATEST;
1418 	permissionOptions.PermissionLevel = EOS.CurrentLobbyData.PermissionLevel;
1419 	EOS_LobbyModification_SetPermissionLevel(LobbyModification, &permissionOptions);*/
1420 
1421 	// build the list of attributes:
1422 	for ( int i = 0; i < EOSFuncs::LobbyData_t::kNumAttributes; ++i )
1423 	{
1424 		EOS_Lobby_AttributeData data = {};
1425 		data.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST;
1426 		data.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING;
1427 		std::pair<std::string, std::string> dataPair = getAttributePair(static_cast<AttributeTypes>(i));
1428 		if ( dataPair.first.compare("empty") == 0 || dataPair.second.compare("empty") == 0 )
1429 		{
1430 			EOSFuncs::logError("updateLobby: invalid data value index: %d first: %s, second: %s", i, dataPair.first.c_str(), dataPair.second.c_str());
1431 			continue;
1432 		}
1433 		else
1434 		{
1435 			EOSFuncs::logInfo("updateLobby: keypair %s | %s", dataPair.first.c_str(), dataPair.second.c_str());
1436 		}
1437 		data.Key = dataPair.first.c_str();
1438 		data.Value.AsUtf8 = dataPair.second.c_str();
1439 
1440 		if ( dataPair.first.compare("MAXPLAYERS") == 0 )
1441 		{
1442 			data.Value.AsInt64 = LobbyAttributes.maxplayersCompatible;
1443 			data.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64;
1444 		}
1445 
1446 		EOS_LobbyModification_AddAttributeOptions addAttributeOptions;
1447 		addAttributeOptions.ApiVersion = EOS_LOBBYMODIFICATION_ADDATTRIBUTE_API_LATEST;
1448 		addAttributeOptions.Visibility = EOS_ELobbyAttributeVisibility::EOS_LAT_PUBLIC;
1449 		addAttributeOptions.Attribute = &(data);
1450 
1451 		result = EOS_LobbyModification_AddAttribute(LobbyModification, &addAttributeOptions);
1452 		if ( result != EOS_EResult::EOS_Success )
1453 		{
1454 			EOSFuncs::logError("updateLobby: Could not add attribute %s. Error code: %d", addAttributeOptions.Attribute->Value.AsUtf8, static_cast<int>(result));
1455 		}
1456 		else
1457 		{
1458 			//EOSFuncs::logInfo("updateLobby: Added key: %s attribute: %s", AddAttributeOptions.Attribute->Key, AddAttributeOptions.Attribute->Value.AsUtf8);
1459 		}
1460 	}
1461 
1462 	// update our client number on the lobby backend
1463 	if ( assignClientnumMemberAttribute(EOS.CurrentUserInfo.getProductUserIdHandle(), 0) )
1464 	{
1465 		modifyLobbyMemberAttributeForCurrentUser();
1466 	}
1467 
1468 	//Trigger lobby update
1469 	EOS_Lobby_UpdateLobbyOptions UpdateOptions;
1470 	UpdateOptions.ApiVersion = EOS_LOBBY_UPDATELOBBY_API_LATEST;
1471 	UpdateOptions.LobbyModificationHandle = EOS.LobbyModificationHandle;
1472 	EOS_Lobby_UpdateLobby(LobbyHandle, &UpdateOptions, nullptr, OnLobbyUpdateFinished);
1473 
1474 	bLobbyHasFullDetailsRead = true;
1475 	return true;
1476 }
1477 
modifyLobbyMemberAttributeForCurrentUser()1478 bool EOSFuncs::LobbyData_t::modifyLobbyMemberAttributeForCurrentUser()
1479 {
1480 	if ( !EOS.CurrentUserInfo.isValid() )
1481 	{
1482 		EOSFuncs::logError("modifyLobbyMemberAttributeForCurrentUser: current user is not valid");
1483 		return false;
1484 	}
1485 
1486 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1487 	EOS_Lobby_UpdateLobbyModificationOptions ModifyOptions;
1488 	ModifyOptions.ApiVersion = EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST;
1489 	ModifyOptions.LobbyId = this->LobbyId.c_str();
1490 	ModifyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
1491 
1492 	if ( EOS.LobbyMemberModificationHandle )
1493 	{
1494 		EOS_LobbyModification_Release(EOS.LobbyMemberModificationHandle);
1495 		EOS.LobbyMemberModificationHandle = nullptr;
1496 	}
1497 	EOS_HLobbyModification LobbyMemberModification = nullptr;
1498 	EOS_EResult result = EOS_Lobby_UpdateLobbyModification(LobbyHandle, &ModifyOptions, &LobbyMemberModification);
1499 	if ( result != EOS_EResult::EOS_Success )
1500 	{
1501 		EOSFuncs::logError("updateLobby: Could not create lobby modification. Error code: %d", static_cast<int>(result));
1502 		return false;
1503 	}
1504 
1505 	EOS.LobbyMemberModificationHandle = LobbyMemberModification;
1506 
1507 	// add attributes for current member
1508 	for ( auto& player : playersInLobby )
1509 	{
1510 		EOS_ProductUserId productId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str());
1511 		if ( productId != EOS.CurrentUserInfo.getProductUserIdHandle() )
1512 		{
1513 			continue;
1514 		}
1515 
1516 		EOS_Lobby_AttributeData memberData;
1517 		memberData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST;
1518 		memberData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64;
1519 		memberData.Key = "CLIENTNUM";
1520 		memberData.Value.AsInt64 = player.clientNumber;
1521 
1522 		EOS_LobbyModification_AddMemberAttributeOptions addMemberData;
1523 		addMemberData.ApiVersion = EOS_LOBBYMODIFICATION_ADDMEMBERATTRIBUTE_API_LATEST;
1524 		addMemberData.Visibility = EOS_ELobbyAttributeVisibility::EOS_LAT_PUBLIC;
1525 		addMemberData.Attribute = &memberData;
1526 
1527 		result = EOS_LobbyModification_AddMemberAttribute(LobbyMemberModification, &addMemberData);
1528 		if ( result != EOS_EResult::EOS_Success )
1529 		{
1530 			EOSFuncs::logError("updateLobby: Could not add member attribute %d. Error code: %d",
1531 				addMemberData.Attribute->Value.AsInt64, static_cast<int>(result));
1532 		}
1533 	}
1534 
1535 	//Trigger lobby update
1536 	EOS_Lobby_UpdateLobbyOptions UpdateOptions;
1537 	UpdateOptions.ApiVersion = EOS_LOBBY_UPDATELOBBY_API_LATEST;
1538 	UpdateOptions.LobbyModificationHandle = EOS.LobbyMemberModificationHandle;
1539 	EOS_Lobby_UpdateLobby(LobbyHandle, &UpdateOptions, nullptr, OnLobbyMemberUpdateFinished);
1540 
1541 	//bLobbyHasFullDetailsRead = true;
1542 	return true;
1543 }
1544 
assignClientnumMemberAttribute(EOS_ProductUserId targetId,int clientNumToSet)1545 bool EOSFuncs::LobbyData_t::assignClientnumMemberAttribute(EOS_ProductUserId targetId, int clientNumToSet)
1546 {
1547 	if ( !EOS.CurrentUserInfo.isValid() )
1548 	{
1549 		EOSFuncs::logError("assignClientnumMemberAttribute: current user is not valid");
1550 		return false;
1551 	}
1552 
1553 	if ( !currentLobbyIsValid() )
1554 	{
1555 		EOSFuncs::logError("assignClientnumMemberAttribute: current lobby is not valid");
1556 		return false;
1557 	}
1558 
1559 	for ( auto& player : playersInLobby )
1560 	{
1561 		EOS_ProductUserId playerId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str());
1562 		if ( playerId && playerId == targetId )
1563 		{
1564 			player.clientNumber = clientNumToSet;
1565 			return true;
1566 		}
1567 	}
1568 	return false;
1569 }
1570 
getClientnumMemberAttribute(EOS_ProductUserId targetId)1571 int EOSFuncs::LobbyData_t::getClientnumMemberAttribute(EOS_ProductUserId targetId)
1572 {
1573 	if ( !EOS.CurrentUserInfo.isValid() )
1574 	{
1575 		EOSFuncs::logError("getClientnumMemberAttribute: current user is not valid");
1576 		return -2;
1577 	}
1578 
1579 	if ( !currentLobbyIsValid() )
1580 	{
1581 		EOSFuncs::logError("getClientnumMemberAttribute: current lobby is not valid");
1582 		return -2;
1583 	}
1584 
1585 	for ( auto& player : playersInLobby )
1586 	{
1587 		EOS_ProductUserId playerId = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str());
1588 		if ( playerId && playerId == targetId )
1589 		{
1590 			return player.clientNumber;
1591 		}
1592 	}
1593 	return -2;
1594 }
1595 
getLobbyAttributes(EOS_HLobbyDetails LobbyDetails)1596 void EOSFuncs::LobbyData_t::getLobbyAttributes(EOS_HLobbyDetails LobbyDetails)
1597 {
1598 	if ( !currentLobbyIsValid() )
1599 	{
1600 		EOSFuncs::logError("getLobbyAttributes: invalid current lobby - no ID set");
1601 		return;
1602 	}
1603 
1604 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1605 
1606 	/*EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions;
1607 	CopyHandleOptions.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST;
1608 	CopyHandleOptions.LobbyId = this->LobbyId.c_str();
1609 	CopyHandleOptions.LocalUserId = EOSFuncs::Helpers_t::productIdFromString(EOS.CurrentUserInfo.getProductUserIdStr());
1610 
1611 	EOS_HLobbyDetails LobbyDetailsHandle = nullptr;
1612 	EOS_EResult result = EOS_Lobby_CopyLobbyDetailsHandle(LobbyHandle, &CopyHandleOptions, &LobbyDetailsHandle);
1613 	if ( result != EOS_EResult::EOS_Success )
1614 	{
1615 		EOSFuncs::logError("getLobbyAttributes: can't get lobby info handle. Error code: %d", static_cast<int>(result));
1616 		return;
1617 	}*/
1618 
1619 	EOS_LobbyDetails_GetAttributeCountOptions CountOptions;
1620 	CountOptions.ApiVersion = EOS_LOBBYDETAILS_GETATTRIBUTECOUNT_API_LATEST;
1621 	int numAttributes = EOS_LobbyDetails_GetAttributeCount(LobbyDetails, &CountOptions);
1622 
1623 	for ( int i = 0; i < numAttributes; ++i )
1624 	{
1625 		EOS_LobbyDetails_CopyAttributeByIndexOptions AttrOptions;
1626 		AttrOptions.ApiVersion = EOS_LOBBYDETAILS_COPYATTRIBUTEBYINDEX_API_LATEST;
1627 		AttrOptions.AttrIndex = i;
1628 
1629 		EOS_Lobby_Attribute* attributePtr = nullptr;
1630 		EOS_EResult result = EOS_LobbyDetails_CopyAttributeByIndex(LobbyDetails, &AttrOptions, &attributePtr);
1631 		if ( result == EOS_EResult::EOS_Success && attributePtr->Data )
1632 		{
1633 			EOS_Lobby_AttributeData data;
1634 			data.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST;
1635 			data.ValueType = attributePtr->Data->ValueType;
1636 			data.Key = attributePtr->Data->Key;
1637 			data.Value.AsUtf8 = attributePtr->Data->Value.AsUtf8;
1638 			this->setLobbyAttributesAfterReading(&data);
1639 		}
1640 		EOS_Lobby_Attribute_Release(attributePtr);
1641 	}
1642 
1643 	if ( !EOS.CurrentLobbyData.currentUserIsOwner() )
1644 	{
1645 		strncpy(EOS.currentLobbyName, LobbyAttributes.lobbyName.c_str(), 31);
1646 	}
1647 	this->bLobbyHasFullDetailsRead = true;
1648 }
1649 
createLobby()1650 void EOSFuncs::createLobby()
1651 {
1652 	/*if ( CurrentLobbyData.currentLobbyIsValid() )
1653 	{
1654 	logInfo("");
1655 	return;
1656 	}*/
1657 
1658 	if ( CurrentLobbyData.bAwaitingLeaveCallback )
1659 	{
1660 		logInfo("createLobby: CurrentLobbyData.bAwaitingLeaveCallback is true");
1661 	}
1662 	if ( CurrentLobbyData.bAwaitingCreationCallback )
1663 	{
1664 		logInfo("createLobby: CurrentLobbyData.bAwaitingCreationCallback is true");
1665 	}
1666 
1667 	CurrentLobbyData.bAwaitingCreationCallback = true;
1668 
1669 	LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle);
1670 	EOS_Lobby_CreateLobbyOptions CreateOptions;
1671 	CreateOptions.ApiVersion = EOS_LOBBY_CREATELOBBY_API_LATEST;
1672 	CreateOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1673 	CreateOptions.MaxLobbyMembers = MAXPLAYERS;
1674 	CreateOptions.PermissionLevel = EOS_ELobbyPermissionLevel::EOS_LPL_PUBLICADVERTISED;
1675 	currentPermissionLevel = EOS_ELobbyPermissionLevel::EOS_LPL_PUBLICADVERTISED;
1676 
1677 	EOS_Lobby_CreateLobby(LobbyHandle, &CreateOptions, nullptr, OnCreateLobbyFinished);
1678 	CurrentLobbyData.MaxPlayers = CreateOptions.MaxLobbyMembers;
1679 	CurrentLobbyData.OwnerProductUserId = CurrentUserInfo.getProductUserIdStr();
1680 	strcpy(EOS.currentLobbyName, "Lobby creation in progress...");
1681 }
1682 
joinLobby(LobbyData_t * lobby)1683 void EOSFuncs::joinLobby(LobbyData_t* lobby)
1684 {
1685 	if ( !lobby )
1686 	{
1687 		return;
1688 	}
1689 	LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle);
1690 
1691 	if ( CurrentLobbyData.currentLobbyIsValid() )
1692 	{
1693 		if ( CurrentLobbyData.LobbyId.compare(lobby->LobbyId) == 0 )
1694 		{
1695 			logInfo("joinLobby: attempting to join current lobby");
1696 			return;
1697 		}
1698 		if ( CurrentLobbyData.bAwaitingLeaveCallback )
1699 		{
1700 			logInfo("joinLobby: CurrentLobbyData.bAwaitingLeaveCallback is true");
1701 		}
1702 		else
1703 		{
1704 			leaveLobby();
1705 			logInfo("joinLobby: leaving current lobby id: %s", CurrentLobbyData.LobbyId.c_str());
1706 		}
1707 	}
1708 	CurrentLobbyData.ClearData();
1709 	CurrentLobbyData.setBasicCurrentLobbyDataFromInitialJoin(lobby);
1710 
1711 	bool errorOnJoin = false;
1712 	if ( CurrentLobbyData.OwnerProductUserId.compare("NULL") == 0 )
1713 	{
1714 		// this is unexpected - perhaps an attempt to join a lobby that was freshly abandoned
1715 		ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_NO_OWNER;
1716 		logError("joinLobby: attempting to join a lobby with a NULL owner: %s, aborting.", CurrentLobbyData.LobbyId.c_str());
1717 		errorOnJoin = true;
1718 	}
1719 	else if ( lobby->LobbyAttributes.isLobbyLoadingSavedGame != loadingsavegame )
1720 	{
1721 		// loading save game, but incorrect assertion from client side.
1722 		if ( loadingsavegame == 0 )
1723 		{
1724 			ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_USING_SAVEGAME;
1725 		}
1726 		else if ( loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame == 0 )
1727 		{
1728 			ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_NOT_USING_SAVEGAME;
1729 		}
1730 		else if ( loadingsavegame > 0 && lobby->LobbyAttributes.isLobbyLoadingSavedGame > 0 )
1731 		{
1732 			ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_WRONG_SAVEGAME;
1733 		}
1734 		else
1735 		{
1736 			ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_UNHANDLED_ERROR;
1737 		}
1738 		errorOnJoin = true;
1739 	}
1740 	else if ( lobby->LobbyAttributes.gameCurrentLevel >= 0 )
1741 	{
1742 		/*if ( lobby->LobbyAttributes.gameCurrentLevel == 0 )
1743 		{
1744 			if (  )
1745 		}
1746 		else
1747 		{
1748 		}*/
1749 		ConnectingToLobbyStatus = LobbyHandler_t::EResult_LobbyFailures::LOBBY_GAME_IN_PROGRESS;
1750 		errorOnJoin = true;
1751 	}
1752 
1753 	if ( errorOnJoin )
1754 	{
1755 		bConnectingToLobbyWindow = false;
1756 		bConnectingToLobby = false;
1757 
1758 		LobbyParameters.clearLobbyToJoin();
1759 		LobbyLeaveCleanup(EOS.CurrentLobbyData);
1760 		return;
1761 	}
1762 
1763 	EOS_Lobby_JoinLobbyOptions JoinOptions;
1764 	JoinOptions.ApiVersion = EOS_LOBBY_JOINLOBBY_API_LATEST;
1765 	JoinOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1766 	JoinOptions.LobbyDetailsHandle = LobbyParameters.lobbyToJoin;
1767 	EOS_Lobby_JoinLobby(LobbyHandle, &JoinOptions, nullptr, OnLobbyJoinCallback);
1768 
1769 	LobbyParameters.clearLobbyToJoin();
1770 }
1771 
leaveLobby()1772 void EOSFuncs::leaveLobby()
1773 {
1774 	//if ( CurrentLobbyData.bAwaitingLeaveCallback )
1775 	//{
1776 	//	// no action needed
1777 	//	logInfo("leaveLobby: attempting to leave lobby with callback already requested, ignoring");
1778 	//	return;
1779 	//}
1780 
1781 	// attempt to destroy the lobby if leaving and we are the owner.
1782 	if ( CurrentLobbyData.currentUserIsOwner() )
1783 	{
1784 		CurrentLobbyData.destroyLobby();
1785 		return;
1786 	}
1787 
1788 	LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle);
1789 
1790 	EOS_Lobby_LeaveLobbyOptions LeaveOptions;
1791 	LeaveOptions.ApiVersion = EOS_LOBBY_LEAVELOBBY_API_LATEST;
1792 	LeaveOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1793 	LeaveOptions.LobbyId = CurrentLobbyData.LobbyId.c_str();
1794 
1795 	CurrentLobbyData.bAwaitingLeaveCallback = true;
1796 	EOS_Lobby_LeaveLobby(LobbyHandle, &LeaveOptions, nullptr, OnLobbyLeaveCallback);
1797 
1798 }
1799 
searchLobbies(LobbyParameters_t::LobbySearchOptions searchType,LobbyParameters_t::LobbyJoinOptions joinOptions,EOS_LobbyId lobbyIdToSearch)1800 void EOSFuncs::searchLobbies(LobbyParameters_t::LobbySearchOptions searchType,
1801 	LobbyParameters_t::LobbyJoinOptions joinOptions, EOS_LobbyId lobbyIdToSearch)
1802 {
1803 	LobbySearchResults.lastResultWasFiltered = false;
1804 
1805 	LobbyHandle = EOS_Platform_GetLobbyInterface(PlatformHandle);
1806 	logInfo("searchLobbies: starting search");
1807 	EOS_Lobby_CreateLobbySearchOptions CreateSearchOptions = {};
1808 	CreateSearchOptions.ApiVersion = EOS_LOBBY_CREATELOBBYSEARCH_API_LATEST;
1809 	CreateSearchOptions.MaxResults = kMaxLobbiesToSearch;
1810 
1811 	EOS_HLobbySearch LobbySearch = nullptr;
1812 	if ( LobbySearchResults.CurrentLobbySearch != nullptr )
1813 	{
1814 		EOS_LobbySearch_Release(LobbySearchResults.CurrentLobbySearch);
1815 		LobbySearchResults.CurrentLobbySearch = nullptr;
1816 	}
1817 
1818 	EOS_EResult result = EOS_Lobby_CreateLobbySearch(LobbyHandle, &CreateSearchOptions, &LobbySearch);
1819 	if ( result != EOS_EResult::EOS_Success )
1820 	{
1821 		logError("searchLobbies: EOS_Lobby_CreateLobbySearch failure: %d", static_cast<int>(result));
1822 		return;
1823 	}
1824 	LobbySearchResults.CurrentLobbySearch = LobbySearch;
1825 	for ( auto& result : LobbySearchResults.results )
1826 	{
1827 		result.ClearData();
1828 	}
1829 	LobbySearchResults.results.clear();
1830 	LobbySearchResults.resultsSortedForDisplay.clear();
1831 
1832 	/*EOS_LobbySearch_SetTargetUserIdOptions SetLobbyOptions = {};
1833 	SetLobbyOptions.ApiVersion = EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST;
1834 	SetLobbyOptions.TargetUserId = CurrentUserInfo.Friends.at(0).UserId;
1835 	Result = EOS_LobbySearch_SetTargetUserId(LobbySearch, &SetLobbyOptions);*/
1836 	EOS_LobbySearch_SetParameterOptions ParamOptions = {};
1837 	ParamOptions.ApiVersion = EOS_LOBBYSEARCH_SETPARAMETER_API_LATEST;
1838 	ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_NOTANYOF;
1839 
1840 	EOS_Lobby_AttributeData AttrData;
1841 	AttrData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST;
1842 	ParamOptions.Parameter = &AttrData;
1843 	AttrData.Key = "VER";
1844 	AttrData.Value.AsUtf8 = "0.0.0";
1845 	AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING;
1846 	EOS_EResult resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);
1847 
1848 	if ( LobbySearchResults.useLobbyCode && strcmp(lobbySearchByCode, "") )
1849 	{
1850 		ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL;
1851 		AttrData.Key = "JOINKEY";
1852 		AttrData.Value.AsUtf8 = lobbySearchByCode;
1853 		AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING;
1854 		resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);
1855 	}
1856 	else
1857 	{
1858 		ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL;
1859 		AttrData.Key = "PERMISSIONLEVEL";
1860 		AttrData.Value.AsUtf8 = "0";
1861 		AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING;
1862 		resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);
1863 	}
1864 
1865 	ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL;
1866 	AttrData.Key = "MAXPLAYERS";
1867 	AttrData.Value.AsInt64 = MAXPLAYERS;
1868 	AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64;
1869 	resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);
1870 
1871 	if ( !LobbySearchResults.showLobbiesInProgress )
1872 	{
1873 		ParamOptions.ComparisonOp = EOS_EComparisonOp::EOS_CO_EQUAL;
1874 		AttrData.Key = "CURRENTLEVEL";
1875 		AttrData.Value.AsUtf8 = "-1";
1876 		AttrData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING;
1877 		resultParameter = EOS_LobbySearch_SetParameter(LobbySearch, &ParamOptions);
1878 	}
1879 
1880 	if ( searchType == LobbyParameters_t::LOBBY_SEARCH_BY_LOBBYID )
1881 	{
1882 		// appends criteria to search for within the normal search function
1883 		EOS_LobbySearch_SetLobbyIdOptions SetLobbyOptions = {};
1884 		SetLobbyOptions.ApiVersion = EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST;
1885 		SetLobbyOptions.LobbyId = lobbyIdToSearch;
1886 		EOS_LobbySearch_SetLobbyId(LobbySearch, &SetLobbyOptions);
1887 	}
1888 
1889 	EOS_LobbySearch_FindOptions FindOptions;
1890 	FindOptions.ApiVersion = EOS_LOBBYSEARCH_FIND_API_LATEST;
1891 	FindOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
1892 
1893 	LobbyParameters.clearLobbySearchOptions();
1894 	LobbyParameters.setLobbySearchOptions(searchType, joinOptions);
1895 	EOS_LobbySearch_Find(LobbySearch, &FindOptions, LobbyParameters.lobbySearchOptions, OnLobbySearchFinished);
1896 }
1897 
destroyLobby()1898 void EOSFuncs::LobbyData_t::destroyLobby()
1899 {
1900 	if ( !currentLobbyIsValid() )
1901 	{
1902 		EOSFuncs::logError("destroyLobby: invalid current lobby - no ID set");
1903 		return;
1904 	}
1905 
1906 	if ( !currentUserIsOwner() )
1907 	{
1908 		EOSFuncs::logError("destroyLobby: current user is not lobby owner");
1909 		return;
1910 	}
1911 
1912 	EOS.LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1913 
1914 	EOS_Lobby_DestroyLobbyOptions DestroyOptions;
1915 	DestroyOptions.ApiVersion = EOS_LOBBY_DESTROYLOBBY_API_LATEST;
1916 	DestroyOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
1917 	DestroyOptions.LobbyId = LobbyId.c_str();
1918 
1919 	EOS_Lobby_DestroyLobby(EOS.LobbyHandle, &DestroyOptions, nullptr, OnDestroyLobbyFinished);
1920 
1921 	EOS.P2PConnectionInfo.resetPeersAndServerData();
1922 	bAwaitingLeaveCallback = false;
1923 	UnsubscribeFromLobbyUpdates();
1924 	ClearData();
1925 }
1926 
updateLobbyDuringGameLoop()1927 void EOSFuncs::LobbyData_t::updateLobbyDuringGameLoop()
1928 {
1929 	if ( !currentLobbyIsValid() )
1930 	{
1931 		return;
1932 	}
1933 	bool doUpdate = false;
1934 	if ( LobbyAttributes.gameCurrentLevel != currentlevel )
1935 	{
1936 		doUpdate = true;
1937 	}
1938 	if ( LobbyAttributes.serverFlags != svFlags )
1939 	{
1940 		doUpdate = true;
1941 	}
1942 
1943 	if ( doUpdate )
1944 	{
1945 		updateLobbyForHost(LOBBY_UPDATE_DURING_GAME);
1946 	}
1947 }
1948 
updateLobby()1949 void EOSFuncs::LobbyData_t::updateLobby()
1950 {
1951 	if ( !EOS.CurrentUserInfo.isValid() )
1952 	{
1953 		EOSFuncs::logError("updateLobby: invalid current user");
1954 		return;
1955 	}
1956 
1957 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1958 
1959 	EOS_Lobby_CopyLobbyDetailsHandleOptions CopyHandleOptions;
1960 	CopyHandleOptions.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST;
1961 	CopyHandleOptions.LobbyId = LobbyId.c_str();
1962 	CopyHandleOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
1963 
1964 	EOS_HLobbyDetails LobbyDetailsHandle = nullptr;
1965 	EOS_EResult result = EOS_Lobby_CopyLobbyDetailsHandle(LobbyHandle, &CopyHandleOptions, &LobbyDetailsHandle);
1966 	if ( result != EOS_EResult::EOS_Success )
1967 	{
1968 		EOSFuncs::logError("OnLobbyUpdateReceived: can't get lobby info handle. Error code: %d", static_cast<int>(result));
1969 		return;
1970 	}
1971 
1972 	EOS.setLobbyDetailsFromHandle(LobbyDetailsHandle, this);
1973 	EOS_LobbyDetails_Release(LobbyDetailsHandle);
1974 }
1975 
1976 
getLobbyMemberInfo(EOS_HLobbyDetails LobbyDetails)1977 void EOSFuncs::LobbyData_t::getLobbyMemberInfo(EOS_HLobbyDetails LobbyDetails)
1978 {
1979 	if ( !currentLobbyIsValid() )
1980 	{
1981 		EOSFuncs::logError("getLobbyMemberInfo: invalid current lobby - no ID set");
1982 		return;
1983 	}
1984 
1985 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
1986 	EOS_LobbyDetails_GetMemberCountOptions MemberCountOptions;
1987 	MemberCountOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERCOUNT_API_LATEST;
1988 
1989 	Uint32 numPlayers = EOS_LobbyDetails_GetMemberCount(LobbyDetails, &MemberCountOptions);
1990 	EOSFuncs::logInfo("getLobbyMemberInfo: NumPlayers in lobby: %d", numPlayers);
1991 
1992 	// so we don't have to wait for a new callback to retrieve names
1993 	std::unordered_map<EOS_ProductUserId, std::string> previousPlayerNames;
1994 	for ( auto& player : playersInLobby )
1995 	{
1996 		EOS_ProductUserId id = EOSFuncs::Helpers_t::productIdFromString(player.memberProductUserId.c_str());
1997 		if ( id )
1998 		{
1999 			previousPlayerNames.insert(std::pair<EOS_ProductUserId, std::string>(id, player.name));
2000 		}
2001 	}
2002 	this->playersInLobby.clear();
2003 
2004 	EOS_LobbyDetails_GetMemberByIndexOptions MemberByIndexOptions;
2005 	MemberByIndexOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERBYINDEX_API_LATEST;
2006 	for ( Uint32 i = 0; i < numPlayers; ++i )
2007 	{
2008 		MemberByIndexOptions.MemberIndex = i;
2009 		EOS_ProductUserId memberId = EOS_LobbyDetails_GetMemberByIndex(LobbyDetails, &MemberByIndexOptions);
2010 		EOSFuncs::logInfo("getLobbyMemberInfo: Lobby Player ID: %s", EOSFuncs::Helpers_t::shortProductIdToString(memberId).c_str());
2011 
2012 		PlayerLobbyData_t newPlayer;
2013 		newPlayer.memberProductUserId = EOSFuncs::Helpers_t::productIdToString(memberId);
2014 		newPlayer.name = "Pending...";
2015 		auto idMapping = EOS.AccountMappings.find(memberId);
2016 		if ( idMapping != EOS.AccountMappings.end() && idMapping->second != nullptr )
2017 		{
2018 			newPlayer.memberEpicAccountId = EOSFuncs::Helpers_t::epicIdToString(idMapping->second);
2019 		}
2020 
2021 		auto previousPlayer = previousPlayerNames.find(memberId);
2022 		if ( previousPlayer != previousPlayerNames.end() )
2023 		{
2024 			// replace "pending..." with the player name we previously knew about.
2025 			newPlayer.name = previousPlayer->second;
2026 		}
2027 
2028 		//member attributes
2029 		EOS_LobbyDetails_GetMemberAttributeCountOptions MemberAttributeCountOptions;
2030 		MemberAttributeCountOptions.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERATTRIBUTECOUNT_API_LATEST;
2031 		MemberAttributeCountOptions.TargetUserId = memberId;
2032 		const Uint32 numAttributes = EOS_LobbyDetails_GetMemberAttributeCount(LobbyDetails, &MemberAttributeCountOptions);
2033 		for ( Uint32 j = 0; j < numAttributes; ++j )
2034 		{
2035 			EOS_LobbyDetails_CopyMemberAttributeByIndexOptions MemberAttributeCopyOptions;
2036 			MemberAttributeCopyOptions.ApiVersion = EOS_LOBBYDETAILS_COPYMEMBERATTRIBUTEBYINDEX_API_LATEST;
2037 			MemberAttributeCopyOptions.AttrIndex = j;
2038 			MemberAttributeCopyOptions.TargetUserId = memberId;
2039 			EOS_Lobby_Attribute* MemberAttribute = nullptr;
2040 			EOS_EResult result = EOS_LobbyDetails_CopyMemberAttributeByIndex(LobbyDetails, &MemberAttributeCopyOptions, &MemberAttribute);
2041 			if ( result != EOS_EResult::EOS_Success )
2042 			{
2043 				EOSFuncs::logError("getLobbyMemberInfo: can't copy member attribute, error code: %d",
2044 					static_cast<int>(result));
2045 				continue;
2046 			}
2047 
2048 			std::string key = MemberAttribute->Data->Key;
2049 			if ( key.compare("CLIENTNUM") == 0 )
2050 			{
2051 				newPlayer.clientNumber = static_cast<int>(MemberAttribute->Data->Value.AsInt64);
2052 				EOSFuncs::logInfo("Read clientnum: %d for user: %s", newPlayer.clientNumber, newPlayer.memberProductUserId.c_str());
2053 			}
2054 			EOS_Lobby_Attribute_Release(MemberAttribute);
2055 		}
2056 
2057 
2058 		this->playersInLobby.push_back(newPlayer);
2059 		this->lobbyMembersQueueToMappingUpdate.push_back(memberId);
2060 	}
2061 
2062 	EOS.queryAccountIdFromProductId(this);
2063 }
2064 
queryLocalExternalAccountId(EOS_EExternalAccountType accountType)2065 void EOSFuncs::queryLocalExternalAccountId(EOS_EExternalAccountType accountType)
2066 {
2067 	EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions = {};
2068 	QueryOptions.ApiVersion = EOS_CONNECT_QUERYPRODUCTUSERIDMAPPINGS_API_LATEST;
2069 	QueryOptions.AccountIdType_DEPRECATED = accountType;
2070 	QueryOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
2071 
2072 	std::vector<EOS_ProductUserId> ids = { EOS.CurrentUserInfo.getProductUserIdHandle() };
2073 	QueryOptions.ProductUserIdCount = ids.size();
2074 	QueryOptions.ProductUserIds = ids.data();
2075 
2076 	for ( EOS_ProductUserId& id : ids )
2077 	{
2078 		auto findExternalAccounts = ExternalAccountMappings.find(id);
2079 		if ( findExternalAccounts == ExternalAccountMappings.end() )
2080 		{
2081 			// if the mapping doesn't exist, add to set. otherwise we already know the account id for the given product id
2082 			ProductIdsAwaitingAccountMappingCallback.insert(id);
2083 		}
2084 		else
2085 		{
2086 			// kick off the user info query since we know the data for the external account
2087 			getExternalAccountUserInfo(id, EOSFuncs::USER_INFO_QUERY_LOCAL);
2088 		}
2089 	}
2090 	EOS_HConnect ConnectHandle = EOS_Platform_GetConnectInterface(PlatformHandle);
2091 	EOS_Connect_QueryProductUserIdMappings(ConnectHandle, &QueryOptions, nullptr, OnQueryAccountMappingsCallback);
2092 }
2093 
queryAccountIdFromProductId(LobbyData_t * lobby)2094 void EOSFuncs::queryAccountIdFromProductId(LobbyData_t* lobby/*, std::vector<EOS_ProductUserId>& accountsToQuery*/)
2095 {
2096 	if ( !lobby )
2097 	{
2098 		return;
2099 	}
2100 	if ( lobby->lobbyMembersQueueToMappingUpdate.empty() )
2101 	{
2102 		return;
2103 	}
2104 	EOS_Connect_QueryProductUserIdMappingsOptions QueryOptions = {};
2105 	QueryOptions.ApiVersion = EOS_CONNECT_QUERYPRODUCTUSERIDMAPPINGS_API_LATEST;
2106 	//QueryOptions.AccountIdType_DEPRECATED = EOS_EExternalAccountType::EOS_EAT_EPIC;
2107 	QueryOptions.LocalUserId = CurrentUserInfo.getProductUserIdHandle();
2108 
2109 	QueryOptions.ProductUserIdCount = lobby->lobbyMembersQueueToMappingUpdate.size();
2110 	QueryOptions.ProductUserIds = lobby->lobbyMembersQueueToMappingUpdate.data();
2111 
2112 	for ( EOS_ProductUserId& id : lobby->lobbyMembersQueueToMappingUpdate )
2113 	{
2114 		auto findEpicAccounts = AccountMappings.find(id);
2115 		auto findExternalAccounts = ExternalAccountMappings.find(id);
2116 		if ( findEpicAccounts == AccountMappings.end() || (*findEpicAccounts).second == nullptr )
2117 		{
2118 			if ( findExternalAccounts == ExternalAccountMappings.end() )
2119 			{
2120 				// if the mapping doesn't exist, add to set. otherwise we already know the account id for the given product id
2121 				ProductIdsAwaitingAccountMappingCallback.insert(id);
2122 			}
2123 			else
2124 			{
2125 				// kick off the user info query since we know the data for the external account
2126 				getExternalAccountUserInfo(id, EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER);
2127 			}
2128 		}
2129 		else
2130 		{
2131 			// kick off the user info query since we know the data.
2132 			getUserInfo(AccountMappings[id], EOSFuncs::USER_INFO_QUERY_LOBBY_MEMBER, 0);
2133 		}
2134 	}
2135 
2136 	EOS_HConnect ConnectHandle = EOS_Platform_GetConnectInterface(PlatformHandle);
2137 	EOS_Connect_QueryProductUserIdMappings(ConnectHandle, &QueryOptions, nullptr, OnQueryAccountMappingsCallback);
2138 
2139 	lobby->lobbyMembersQueueToMappingUpdate.clear();
2140 }
2141 
getAttributePair(AttributeTypes type)2142 std::pair<std::string, std::string> EOSFuncs::LobbyData_t::getAttributePair(AttributeTypes type)
2143 {
2144 	//char buffer[128] = "";
2145 	std::pair<std::string, std::string> attributePair;
2146 	attributePair.first = "empty";
2147 	attributePair.second = "empty";
2148 	switch ( type )
2149 	{
2150 		case LOBBY_NAME:
2151 			attributePair.first = "NAME";
2152 			attributePair.second = this->LobbyAttributes.lobbyName;
2153 			break;
2154 		case GAME_VERSION:
2155 			attributePair.first = "VER";
2156 			attributePair.second = this->LobbyAttributes.gameVersion;
2157 			break;
2158 		case GAME_MAXPLAYERS:
2159 			attributePair.first = "MAXPLAYERS";
2160 			attributePair.second = "";
2161 			break;
2162 		case SERVER_FLAGS:
2163 			attributePair.first = "SVFLAGS";
2164 			char svFlagsChar[32];
2165 			snprintf(svFlagsChar, 31, "%d", this->LobbyAttributes.serverFlags);
2166 			attributePair.second = svFlagsChar;
2167 			break;
2168 		case LOADING_SAVEGAME:
2169 			attributePair.first = "LOADINGSAVEGAME";
2170 			attributePair.second = std::to_string(this->LobbyAttributes.isLobbyLoadingSavedGame);
2171 			break;
2172 		case GAME_MODS:
2173 			attributePair.first = "SVNUMMODS";
2174 			attributePair.second = "0";
2175 			break;
2176 		case CREATION_TIME:
2177 			attributePair.first = "CREATETIME";
2178 			attributePair.second = std::to_string(this->LobbyAttributes.lobbyCreationTime);
2179 			break;
2180 		case GAME_CURRENT_LEVEL:
2181 			attributePair.first = "CURRENTLEVEL";
2182 			attributePair.second = std::to_string(this->LobbyAttributes.gameCurrentLevel);
2183 			break;
2184 		case GAME_JOIN_KEY:
2185 			attributePair.first = "JOINKEY";
2186 			attributePair.second = this->LobbyAttributes.gameJoinKey;
2187 			break;
2188 		case LOBBY_PERMISSION_LEVEL:
2189 			attributePair.first = "PERMISSIONLEVEL";
2190 			char permissionLevel[32];
2191 			snprintf(permissionLevel, 31, "%d", this->LobbyAttributes.PermissionLevel);
2192 			attributePair.second = permissionLevel;
2193 			break;
2194 		default:
2195 			break;
2196 	}
2197 	return attributePair;
2198 }
2199 
setLobbyAttributesAfterReading(EOS_Lobby_AttributeData * data)2200 void EOSFuncs::LobbyData_t::setLobbyAttributesAfterReading(EOS_Lobby_AttributeData* data)
2201 {
2202 	std::string keyName = data->Key;
2203 	if ( keyName.compare("NAME") == 0 )
2204 	{
2205 		this->LobbyAttributes.lobbyName = data->Value.AsUtf8;
2206 	}
2207 	else if ( keyName.compare("VER") == 0 )
2208 	{
2209 		this->LobbyAttributes.gameVersion = data->Value.AsUtf8;
2210 	}
2211 	else if ( keyName.compare("MAXPLAYERS") == 0 )
2212 	{
2213 		this->LobbyAttributes.maxplayersCompatible = data->Value.AsInt64;
2214 	}
2215 	else if ( keyName.compare("SVFLAGS") == 0 )
2216 	{
2217 		this->LobbyAttributes.serverFlags = std::stoi(data->Value.AsUtf8);
2218 	}
2219 	else if ( keyName.compare("LOADINGSAVEGAME") == 0 )
2220 	{
2221 		this->LobbyAttributes.isLobbyLoadingSavedGame = std::stoul(data->Value.AsUtf8);
2222 	}
2223 	else if ( keyName.compare("SVNUMMODS") == 0 )
2224 	{
2225 		this->LobbyAttributes.numServerMods = std::stoi(data->Value.AsUtf8);
2226 	}
2227 	else if ( keyName.compare("CREATETIME") == 0 )
2228 	{
2229 		this->LobbyAttributes.lobbyCreationTime = std::stoll(data->Value.AsUtf8);
2230 	}
2231 	else if ( keyName.compare("CURRENTLEVEL") == 0 )
2232 	{
2233 		this->LobbyAttributes.gameCurrentLevel = std::stoi(data->Value.AsUtf8);
2234 	}
2235 	else if ( keyName.compare("JOINKEY") == 0 )
2236 	{
2237 		this->LobbyAttributes.gameJoinKey = data->Value.AsUtf8;
2238 	}
2239 	else if ( keyName.compare("PERMISSIONLEVEL") == 0 )
2240 	{
2241 		this->LobbyAttributes.PermissionLevel = std::stoi(data->Value.AsUtf8);
2242 	}
2243 }
2244 
getPeerIdFromIndex(int index) const2245 EOS_ProductUserId EOSFuncs::P2PConnectionInfo_t::getPeerIdFromIndex(int index) const
2246 {
2247 	if ( index == 0 && multiplayer == CLIENT )
2248 	{
2249 		return serverProductId;
2250 	}
2251 
2252 	for ( auto& pair : peerProductIds )
2253 	{
2254 		if ( pair.second == index )
2255 		{
2256 			return pair.first;
2257 		}
2258 	}
2259 	//logError("getPeerIdFromIndex: no peer with index: %d", index);
2260 	return nullptr;
2261 }
2262 
getIndexFromPeerId(EOS_ProductUserId id) const2263 int EOSFuncs::P2PConnectionInfo_t::getIndexFromPeerId(EOS_ProductUserId id) const
2264 {
2265 	if ( !id )
2266 	{
2267 		return 0;
2268 	}
2269 	for ( auto& pair : peerProductIds )
2270 	{
2271 		if ( pair.first == id )
2272 		{
2273 			return pair.second;
2274 		}
2275 	}
2276 	logError("getPeerIdFromIndex: no peer with id: %s", EOSFuncs::Helpers_t::productIdToString(id));
2277 	return 0;
2278 }
2279 
isPeerIndexed(EOS_ProductUserId id)2280 bool EOSFuncs::P2PConnectionInfo_t::isPeerIndexed(EOS_ProductUserId id)
2281 {
2282 	if ( id == serverProductId )
2283 	{
2284 		return true;
2285 	}
2286 	for ( auto& pair : peerProductIds )
2287 	{
2288 		if ( pair.first == id )
2289 		{
2290 			return true;
2291 		}
2292 	}
2293 	return false;
2294 }
2295 
assignPeerIndex(EOS_ProductUserId id,int index)2296 bool EOSFuncs::P2PConnectionInfo_t::assignPeerIndex(EOS_ProductUserId id, int index)
2297 {
2298 	for ( auto& pair : peerProductIds )
2299 	{
2300 		if ( pair.first == id )
2301 		{
2302 			pair.second = index;
2303 			return true;
2304 		}
2305 	}
2306 	return false;
2307 }
2308 
isPeerStillValid(int index) const2309 bool EOSFuncs::P2PConnectionInfo_t::isPeerStillValid(int index) const
2310 {
2311 	if ( !getPeerIdFromIndex(index) )
2312 	{
2313 		return false;
2314 	}
2315 	if ( !Helpers_t::productIdIsValid(getPeerIdFromIndex(index)) )
2316 	{
2317 		return false;
2318 	}
2319 	return true;
2320 }
2321 
resetPeersAndServerData()2322 void EOSFuncs::P2PConnectionInfo_t::resetPeersAndServerData()
2323 {
2324 	EOS_P2P_CloseConnectionsOptions closeOptions = {};
2325 	closeOptions.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST;
2326 	closeOptions.LocalUserId = EOS.CurrentUserInfo.getProductUserIdHandle();
2327 	EOS_P2P_SocketId SocketId = {};
2328 	SocketId.ApiVersion = EOS_P2P_SOCKETID_API_LATEST;
2329 	strncpy(SocketId.SocketName, "CHAT", 5);
2330 	closeOptions.SocketId = &SocketId;
2331 
2332 	EOS_EResult result = EOS_P2P_CloseConnections(EOS_Platform_GetP2PInterface(EOS.PlatformHandle), &closeOptions);
2333 	EOSFuncs::logInfo("resetPeersAndServerData: closing P2P connections, result: %d", static_cast<int>(result));
2334 
2335 	peerProductIds.clear();
2336 	serverProductId = nullptr;
2337 }
2338 
SubscribeToLobbyUpdates()2339 void EOSFuncs::LobbyData_t::SubscribeToLobbyUpdates()
2340 {
2341 	UnsubscribeFromLobbyUpdates();
2342 
2343 	EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
2344 
2345 	EOS_Lobby_AddNotifyLobbyUpdateReceivedOptions UpdateNotifyOptions;
2346 	UpdateNotifyOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYUPDATERECEIVED_API_LATEST;
2347 
2348 	LobbyUpdateNotification = EOS_Lobby_AddNotifyLobbyUpdateReceived(LobbyHandle, &UpdateNotifyOptions, nullptr, OnLobbyUpdateReceived);
2349 
2350 	EOS_Lobby_AddNotifyLobbyMemberUpdateReceivedOptions MemberUpdateOptions;
2351 	MemberUpdateOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERUPDATERECEIVED_API_LATEST;
2352 	LobbyMemberUpdateNotification = EOS_Lobby_AddNotifyLobbyMemberUpdateReceived(LobbyHandle, &MemberUpdateOptions, nullptr, OnMemberUpdateReceived);
2353 
2354 	EOS_Lobby_AddNotifyLobbyMemberStatusReceivedOptions MemberStatusOptions;
2355 	MemberStatusOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERSTATUSRECEIVED_API_LATEST;
2356 	LobbyMemberStatusNotification = EOS_Lobby_AddNotifyLobbyMemberStatusReceived(LobbyHandle, &MemberStatusOptions, nullptr, OnMemberStatusReceived);
2357 
2358 	EOSFuncs::logInfo("SubscribeToLobbyUpdates: %s", this->LobbyId.c_str());
2359 }
2360 
UnsubscribeFromLobbyUpdates()2361 void EOSFuncs::LobbyData_t::UnsubscribeFromLobbyUpdates()
2362 {
2363 	if ( LobbyUpdateNotification != EOS_INVALID_NOTIFICATIONID )
2364 	{
2365 		EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
2366 
2367 		EOS_Lobby_RemoveNotifyLobbyUpdateReceived(LobbyHandle, LobbyUpdateNotification);
2368 		LobbyUpdateNotification = EOS_INVALID_NOTIFICATIONID;
2369 	}
2370 
2371 	if ( LobbyMemberUpdateNotification != EOS_INVALID_NOTIFICATIONID )
2372 	{
2373 		EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
2374 
2375 		EOS_Lobby_RemoveNotifyLobbyMemberUpdateReceived(LobbyHandle, LobbyMemberUpdateNotification);
2376 		LobbyMemberUpdateNotification = EOS_INVALID_NOTIFICATIONID;
2377 	}
2378 
2379 	if ( LobbyMemberStatusNotification != EOS_INVALID_NOTIFICATIONID )
2380 	{
2381 		EOS_HLobby LobbyHandle = EOS_Platform_GetLobbyInterface(EOS.PlatformHandle);
2382 
2383 		EOS_Lobby_RemoveNotifyLobbyMemberStatusReceived(LobbyHandle, LobbyMemberStatusNotification);
2384 		LobbyMemberStatusNotification = EOS_INVALID_NOTIFICATIONID;
2385 	}
2386 
2387 	EOSFuncs::logInfo("UnsubscribeFromLobbyUpdates: %s", this->LobbyId.c_str());
2388 }
2389 
OnAchievementQueryComplete(const EOS_Achievements_OnQueryDefinitionsCompleteCallbackInfo * data)2390 void EOS_CALL EOSFuncs::OnAchievementQueryComplete(const EOS_Achievements_OnQueryDefinitionsCompleteCallbackInfo* data)
2391 {
2392 	assert(data != NULL);
2393 
2394 	EOS.Achievements.definitionsAwaitingCallback = false;
2395 
2396 	if ( data->ResultCode != EOS_EResult::EOS_Success )
2397 	{
2398 		logError("OnAchievementQueryComplete: Failed to load achievements");
2399 		return;
2400 	}
2401 
2402 	EOS_Achievements_GetAchievementDefinitionCountOptions AchievementDefinitionsCountOptions = {};
2403 	AchievementDefinitionsCountOptions.ApiVersion = EOS_ACHIEVEMENTS_GETACHIEVEMENTDEFINITIONCOUNT_API_LATEST;
2404 
2405 	uint32_t AchievementDefinitionsCount = EOS_Achievements_GetAchievementDefinitionCount(EOS.AchievementsHandle, &AchievementDefinitionsCountOptions);
2406 
2407 	EOS_Achievements_CopyAchievementDefinitionV2ByIndexOptions CopyOptions = {};
2408 	CopyOptions.ApiVersion = EOS_ACHIEVEMENTS_COPYDEFINITIONV2BYACHIEVEMENTID_API_LATEST;
2409 
2410 	for ( CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementDefinitionsCount; ++CopyOptions.AchievementIndex )
2411 	{
2412 		EOS_Achievements_DefinitionV2* AchievementDef = NULL;
2413 
2414 		EOS_EResult CopyAchievementDefinitionsResult = EOS_Achievements_CopyAchievementDefinitionV2ByIndex(EOS.AchievementsHandle, &CopyOptions, &AchievementDef);
2415 		if ( CopyAchievementDefinitionsResult != EOS_EResult::EOS_Success )
2416 		{
2417 			logError("CopyAchievementDefinitions Failure!");
2418 			return;
2419 		}
2420 
2421 		if ( AchievementDef->bIsHidden )
2422 		{
2423 			achievementHidden.emplace(std::string(AchievementDef->AchievementId));
2424 		}
2425 
2426 		if ( AchievementDef->UnlockedDisplayName )
2427 		{
2428 			achievementNames.emplace(std::make_pair(
2429 				std::string(AchievementDef->AchievementId),
2430 				std::string(AchievementDef->UnlockedDisplayName)));
2431 		}
2432 
2433 		if ( AchievementDef->UnlockedDescription )
2434 		{
2435 			auto result = achievementDesc.emplace(std::make_pair(
2436 				std::string(AchievementDef->AchievementId),
2437 				std::string(AchievementDef->UnlockedDescription)));
2438 			if ( result.second == true ) // insertion success
2439 			{
2440 				if ( result.first->second.back() != '.' ) // add punctuation if missing.
2441 				{
2442 					result.first->second += '.';
2443 				}
2444 			}
2445 		}
2446 
2447 		// Release Achievement Definition
2448 		EOS_Achievements_DefinitionV2_Release(AchievementDef);
2449 	}
2450 
2451 	EOS.Achievements.definitionsLoaded = true;
2452 	EOS.Achievements.sortAchievementsForDisplay();
2453 
2454 	logInfo("Successfully loaded achievements");
2455 }
2456 
OnPlayerAchievementQueryComplete(const EOS_Achievements_OnQueryPlayerAchievementsCompleteCallbackInfo * data)2457 void EOSFuncs::OnPlayerAchievementQueryComplete(const EOS_Achievements_OnQueryPlayerAchievementsCompleteCallbackInfo* data)
2458 {
2459 	assert(data != NULL);
2460 
2461 	EOS.Achievements.playerDataAwaitingCallback = false;
2462 
2463 	if ( data->ResultCode != EOS_EResult::EOS_Success )
2464 	{
2465 		logError("OnPlayerAchievementQueryComplete: Failed to load player achievement status");
2466 		return;
2467 	}
2468 
2469 	EOS_Achievements_GetPlayerAchievementCountOptions AchievementsCountOptions = {};
2470 	AchievementsCountOptions.ApiVersion = EOS_ACHIEVEMENTS_GETPLAYERACHIEVEMENTCOUNT_API_LATEST;
2471 	AchievementsCountOptions.UserId = EOS.CurrentUserInfo.getProductUserIdHandle();
2472 
2473 	uint32_t AchievementsCount = EOS_Achievements_GetPlayerAchievementCount(EOS.AchievementsHandle, &AchievementsCountOptions);
2474 
2475 	EOS_Achievements_CopyPlayerAchievementByIndexOptions CopyOptions = {};
2476 	CopyOptions.ApiVersion = EOS_ACHIEVEMENTS_COPYPLAYERACHIEVEMENTBYINDEX_API_LATEST;
2477 	CopyOptions.UserId = EOS.CurrentUserInfo.getProductUserIdHandle();
2478 
2479 	for ( CopyOptions.AchievementIndex = 0; CopyOptions.AchievementIndex < AchievementsCount; ++CopyOptions.AchievementIndex )
2480 	{
2481 		EOS_Achievements_PlayerAchievement* PlayerAchievement = NULL;
2482 
2483 		EOS_EResult CopyPlayerAchievementResult = EOS_Achievements_CopyPlayerAchievementByIndex(EOS.AchievementsHandle, &CopyOptions, &PlayerAchievement);
2484 		if ( CopyPlayerAchievementResult == EOS_EResult::EOS_Success )
2485 		{
2486 			if ( PlayerAchievement->UnlockTime != EOS_ACHIEVEMENTS_ACHIEVEMENT_UNLOCKTIME_UNDEFINED )
2487 			{
2488 				achievementUnlockedLookup.insert(std::string(PlayerAchievement->AchievementId));
2489 
2490 				auto find = achievementUnlockTime.find(PlayerAchievement->AchievementId);
2491 				if ( find == achievementUnlockTime.end() )
2492 				{
2493 					achievementUnlockTime.emplace(std::make_pair(std::string(PlayerAchievement->AchievementId), PlayerAchievement->UnlockTime));
2494 				}
2495 				else
2496 				{
2497 					find->second = PlayerAchievement->UnlockTime;
2498 				}
2499 			}
2500 
2501 			if ( PlayerAchievement->StatInfoCount > 0 )
2502 			{
2503 				for ( int statNum = 0; statNum < NUM_STEAM_STATISTICS; ++statNum )
2504 				{
2505 					if ( steamStatAchStringsAndMaxVals[statNum].first.compare(PlayerAchievement->AchievementId) == 0 )
2506 					{
2507 						auto find = achievementProgress.find(PlayerAchievement->AchievementId);
2508 						if ( find == achievementProgress.end() )
2509 						{
2510 							achievementProgress.emplace(std::make_pair(std::string(PlayerAchievement->AchievementId), statNum));
2511 						}
2512 						else
2513 						{
2514 							find->second = statNum;
2515 						}
2516 						break;
2517 					}
2518 				}
2519 			}
2520 		}
2521 		EOS_Achievements_PlayerAchievement_Release(PlayerAchievement);
2522 	}
2523 
2524 	EOS.Achievements.playerDataLoaded = true;
2525 	EOS.Achievements.sortAchievementsForDisplay();
2526 
2527 	logInfo("OnPlayerAchievementQueryComplete: success");
2528 }
2529 
sortAchievementsForDisplay()2530 void EOSFuncs::Achievements_t::sortAchievementsForDisplay()
2531 {
2532 	if ( !definitionsLoaded )
2533 	{
2534 		return;
2535 	}
2536 
2537 	// sort achievements list
2538 	achievementNamesSorted.clear();
2539 	Comparator compFunctor =
2540 		[](std::pair<std::string, std::string> lhs, std::pair<std::string, std::string> rhs)
2541 	{
2542 		bool ach1 = achievementUnlocked(lhs.first.c_str());
2543 		bool ach2 = achievementUnlocked(rhs.first.c_str());
2544 		bool lhsAchIsHidden = (achievementHidden.find(lhs.first) != achievementHidden.end());
2545 		bool rhsAchIsHidden = (achievementHidden.find(rhs.first) != achievementHidden.end());
2546 		if ( ach1 && !ach2 )
2547 		{
2548 			return true;
2549 		}
2550 		else if ( !ach1 && ach2 )
2551 		{
2552 			return false;
2553 		}
2554 		else if ( !ach1 && !ach2 && (lhsAchIsHidden || rhsAchIsHidden) )
2555 		{
2556 			if ( lhsAchIsHidden && rhsAchIsHidden )
2557 			{
2558 				return lhs.second < rhs.second;
2559 			}
2560 			if ( !lhsAchIsHidden )
2561 			{
2562 				return true;
2563 			}
2564 			if ( !rhsAchIsHidden )
2565 			{
2566 				return false;
2567 			}
2568 			return lhs.second < rhs.second;
2569 		}
2570 		else
2571 		{
2572 			return lhs.second < rhs.second;
2573 		}
2574 	};
2575 	std::set<std::pair<std::string, std::string>, Comparator> sorted(achievementNames.begin(), achievementNames.end(), compFunctor);
2576 	achievementNamesSorted.swap(sorted);
2577 }
2578 
OnIngestStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo * data)2579 void EOSFuncs::OnIngestStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo* data)
2580 {
2581 	assert(data != NULL);
2582 	if ( data->ResultCode == EOS_EResult::EOS_Success )
2583 	{
2584 		logInfo("Stats updated");
2585 	}
2586 	else
2587 	{
2588 		logError("OnIngestStatComplete: Callback failure: %d", static_cast<int>(data->ResultCode));
2589 	}
2590 }
2591 
initAchievements()2592 bool EOSFuncs::initAchievements()
2593 {
2594 	logInfo("Initializing EOS achievements");
2595 	if ( !PlatformHandle )
2596 	{
2597 		return false;
2598 	}
2599 	if ( (AchievementsHandle = EOS_Platform_GetAchievementsInterface(PlatformHandle)) == nullptr )
2600 	{
2601 		return false;
2602 	}
2603 	if ( (StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle)) == nullptr )
2604 	{
2605 		return false;
2606 	}
2607 	return true;
2608 }
2609 
OnUnlockAchievement(const EOS_Achievements_OnUnlockAchievementsCompleteCallbackInfo * data)2610 void EOS_CALL EOSFuncs::OnUnlockAchievement(const EOS_Achievements_OnUnlockAchievementsCompleteCallbackInfo* data)
2611 {
2612 	assert(data != NULL);
2613 	//int64_t t = (int64_t)time(NULL);
2614 	//achievementUnlockTime.emplace(std::make_pair(std::string((const char*)data->ClientData), t));
2615 	if ( data->ResultCode == EOS_EResult::EOS_Success )
2616 	{
2617 		logInfo("EOS achievement successfully unlocked");
2618 		return;
2619 	}
2620 	else
2621 	{
2622 		logError("OnUnlockAchievement: Callback failure: %d", static_cast<int>(data->ResultCode));
2623 		return;
2624 	}
2625 }
2626 
loadAchievementData()2627 void EOSFuncs::loadAchievementData()
2628 {
2629 	if ( Achievements.definitionsAwaitingCallback || Achievements.playerDataAwaitingCallback )
2630 	{
2631 		return;
2632 	}
2633 	if ( !CurrentUserInfo.isValid() || !CurrentUserInfo.isLoggedIn() )
2634 	{
2635 		return;
2636 	}
2637 
2638 	Achievements.bAchievementsInit = true;
2639 
2640 	logInfo("Loading EOS achievements");
2641 
2642 	if ( !Achievements.definitionsLoaded )
2643 	{
2644 		Achievements.definitionsAwaitingCallback = true;
2645 		achievementNames.clear();
2646 		achievementDesc.clear();
2647 		achievementHidden.clear();
2648 		EOS_Achievements_QueryDefinitionsOptions Options = {};
2649 		Options.ApiVersion = EOS_ACHIEVEMENTS_QUERYDEFINITIONS_API_LATEST;
2650 		Options.EpicUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str());
2651 		Options.UserId = CurrentUserInfo.getProductUserIdHandle();
2652 		Options.HiddenAchievementsCount = 0;
2653 		Options.HiddenAchievementIds = nullptr;
2654 		EOS_Achievements_QueryDefinitions(AchievementsHandle, &Options, nullptr, OnAchievementQueryComplete);
2655 
2656 		EOS.StatGlobalManager.queryGlobalStatUser();
2657 	}
2658 
2659 	Achievements.playerDataAwaitingCallback = true;
2660 	//achievementProgress.clear();
2661 	//achievementUnlockTime.clear();
2662 	EOS_Achievements_QueryPlayerAchievementsOptions PlayerAchievementOptions = {};
2663 	PlayerAchievementOptions.ApiVersion = EOS_ACHIEVEMENTS_QUERYPLAYERACHIEVEMENTS_API_LATEST;
2664 	PlayerAchievementOptions.UserId = CurrentUserInfo.getProductUserIdHandle();
2665 	EOS_Achievements_QueryPlayerAchievements(AchievementsHandle, &PlayerAchievementOptions, nullptr, OnPlayerAchievementQueryComplete);
2666 
2667 	EOS.queryAllStats();
2668 }
2669 
unlockAchievement(const char * name)2670 void EOSFuncs::unlockAchievement(const char* name)
2671 {
2672 	//logInfo("unlocking EOS achievement '%s'", name);
2673 	EOS_Achievements_UnlockAchievementsOptions UnlockAchievementsOptions = {};
2674 	UnlockAchievementsOptions.ApiVersion = EOS_ACHIEVEMENTS_UNLOCKACHIEVEMENTS_API_LATEST;
2675 	UnlockAchievementsOptions.UserId = CurrentUserInfo.getProductUserIdHandle();
2676 	UnlockAchievementsOptions.AchievementsCount = 1;
2677 	UnlockAchievementsOptions.AchievementIds = &name;
2678 	EOS_Achievements_UnlockAchievements(AchievementsHandle, &UnlockAchievementsOptions, nullptr, OnUnlockAchievement);
2679 	UIToastNotificationManager.createAchievementNotification(name);
2680 }
2681 
ingestStat(int stat_num,int value)2682 void EOSFuncs::ingestStat(int stat_num, int value)
2683 {
2684 	//logInfo("updating EOS stat '%s'", g_SteamStats[stat_num].m_pchStatName);
2685 
2686 	StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle);
2687 
2688 	EOS_Stats_IngestData StatsToIngest[1];
2689 	StatsToIngest[0].ApiVersion = EOS_STATS_INGESTDATA_API_LATEST;
2690 	StatsToIngest[0].StatName = g_SteamStats[stat_num].m_pchStatName;
2691 	StatsToIngest[0].IngestAmount = value;
2692 
2693 	EOS_Stats_IngestStatOptions Options = {};
2694 	Options.ApiVersion = EOS_STATS_INGESTSTAT_API_LATEST;
2695 	Options.Stats = StatsToIngest;
2696 	Options.StatsCount = sizeof(StatsToIngest) / sizeof(StatsToIngest[0]);
2697 	Options.UserId = CurrentUserInfo.getProductUserIdHandle();
2698 	EOS_Stats_IngestStat(StatsHandle, &Options, nullptr, OnIngestStatComplete);
2699 }
2700 
OnIngestGlobalStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo * data)2701 static void EOS_CALL OnIngestGlobalStatComplete(const EOS_Stats_IngestStatCompleteCallbackInfo* data)
2702 {
2703 	assert(data != NULL);
2704 	if ( data->ResultCode == EOS_EResult::EOS_Success )
2705 	{
2706 		EOSFuncs::logInfo("Successfully stored global stats");
2707 		for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i )
2708 		{
2709 			g_SteamGlobalStats[i].m_iValue = 0;
2710 		}
2711 		return;
2712 	}
2713 	else if ( data->ResultCode == EOS_EResult::EOS_TooManyRequests )
2714 	{
2715 		return;
2716 	}
2717 	else
2718 	{
2719 		EOSFuncs::logError("OnIngestGlobalStatComplete: Callback failure: %d", static_cast<int>(data->ResultCode));
2720 	}
2721 	EOS.StatGlobalManager.bDataQueued = true;
2722 }
2723 
queueGlobalStatUpdate(int stat_num,int value)2724 void EOSFuncs::queueGlobalStatUpdate(int stat_num, int value)
2725 {
2726 	if ( stat_num <= STEAM_GSTAT_INVALID || stat_num >= NUM_GLOBAL_STEAM_STATISTICS )
2727 	{
2728 		return;
2729 	}
2730 	if ( StatGlobalManager.bIsDisabled )
2731 	{
2732 		return;
2733 	}
2734 	g_SteamGlobalStats[stat_num].m_iValue += value;
2735 	StatGlobalManager.bDataQueued = true;
2736 }
2737 
updateQueuedStats()2738 void EOSFuncs::StatGlobal_t::updateQueuedStats()
2739 {
2740 	if ( !bDataQueued || bIsDisabled )
2741 	{
2742 		return;
2743 	}
2744 	if ( (ticks - lastUpdateTicks) < TICKS_PER_SECOND * 30 )
2745 	{
2746 		return;
2747 	}
2748 	EOS.ingestGlobalStats();
2749 	lastUpdateTicks = ticks;
2750 	bDataQueued = false;
2751 }
2752 
ingestGlobalStats()2753 void EOSFuncs::ingestGlobalStats()
2754 {
2755 	if ( StatGlobalManager.bIsDisabled )
2756 	{
2757 		return;
2758 	}
2759 
2760 	Uint32 numStats = 0;
2761 	std::vector<std::string> StatNames;
2762 	for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i )
2763 	{
2764 		if ( g_SteamGlobalStats[i].m_iValue > 0 )
2765 		{
2766 			StatNames.push_back(g_SteamGlobalStats[i].m_pchStatName);
2767 			++numStats;
2768 		}
2769 	}
2770 
2771 	if ( numStats == 0 )
2772 	{
2773 		return;
2774 	}
2775 
2776 	EOS_Stats_IngestData* StatsToIngest = new EOS_Stats_IngestData[numStats];
2777 	Uint32 currentIndex = 0;
2778 	for ( Uint32 i = 0; i < NUM_GLOBAL_STEAM_STATISTICS && currentIndex < StatNames.size(); ++i )
2779 	{
2780 		if ( g_SteamGlobalStats[i].m_iValue > 0 )
2781 		{
2782 			StatsToIngest[currentIndex].ApiVersion = EOS_STATS_INGESTDATA_API_LATEST;
2783 			StatsToIngest[currentIndex].StatName = StatNames[currentIndex].c_str();
2784 			StatsToIngest[currentIndex].IngestAmount = g_SteamGlobalStats[i].m_iValue;
2785 			//logInfo("Updated %s | %d", StatsToIngest[currentIndex].StatName, StatsToIngest[currentIndex].IngestAmount);
2786 			++currentIndex;
2787 		}
2788 	}
2789 
2790 	EOS_Stats_IngestStatOptions Options = {};
2791 	Options.ApiVersion = EOS_STATS_INGESTSTAT_API_LATEST;
2792 	Options.Stats = StatsToIngest;
2793 	Options.StatsCount = numStats;
2794 	Options.UserId = StatGlobalManager.getProductUserIdHandle();
2795 
2796 	EOS_Stats_IngestStat(EOS_Platform_GetStatsInterface(ServerPlatformHandle), &Options, nullptr, OnIngestGlobalStatComplete);
2797 
2798 	delete[] StatsToIngest;
2799 }
2800 
OnQueryAllStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo * data)2801 void EOS_CALL EOSFuncs::OnQueryAllStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo* data)
2802 {
2803 	if ( !data )
2804 	{
2805 		EOSFuncs::logError("OnQueryAllStatsCallback: null data");
2806 		return;
2807 	}
2808 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
2809 	{
2810 
2811 		EOS.StatsHandle = EOS_Platform_GetStatsInterface(EOS.PlatformHandle);
2812 		EOS_Stats_GetStatCountOptions StatCountOptions = {};
2813 		StatCountOptions.ApiVersion = EOS_STATS_GETSTATCOUNT_API_LATEST;
2814 		StatCountOptions.UserId = EOS.CurrentUserInfo.getProductUserIdHandle();
2815 
2816 		Uint32 numStats = EOS_Stats_GetStatsCount(EOS.StatsHandle, &StatCountOptions);
2817 
2818 		EOSFuncs::logInfo("OnQueryAllStatsCallback: read %d stats", numStats);
2819 
2820 		EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions = {};
2821 		CopyByIndexOptions.ApiVersion = EOS_STATS_COPYSTATBYINDEX_API_LATEST;
2822 		CopyByIndexOptions.UserId = EOS.CurrentUserInfo.getProductUserIdHandle();
2823 
2824 		EOS_Stats_Stat* copyStat = NULL;
2825 
2826 		for ( Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i )
2827 		{
2828 			g_SteamStats[i].m_iValue = 0;
2829 		}
2830 
2831 		for ( CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex )
2832 		{
2833 			EOS_EResult result = EOS_Stats_CopyStatByIndex(EOS.StatsHandle, &CopyByIndexOptions, &copyStat);
2834 			if ( result == EOS_EResult::EOS_Success && copyStat )
2835 			{
2836 				SteamStat_t* statLookup = nullptr;
2837 				if ( statLookup = EOS.getStatStructFromString(std::string(copyStat->Name)) )
2838 				{
2839 					statLookup->m_iValue = copyStat->Value;
2840 				}
2841 				//EOSFuncs::logInfo("OnQueryAllStatsCallback: stat %s | %d", copyStat->Name, copyStat->Value);
2842 				EOS_Stats_Stat_Release(copyStat);
2843 			}
2844 		}
2845 	}
2846 	else
2847 	{
2848 		EOSFuncs::logError("OnQueryAllStatsCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
2849 	}
2850 }
2851 
getStatStructFromString(const std::string & str)2852 SteamStat_t* EOSFuncs::getStatStructFromString(const std::string& str)
2853 {
2854 	if ( !statMappings.size() )
2855 	{
2856 		EOSFuncs::logError("getStatStructFromString: Empty stat mappings");
2857 		return nullptr;
2858 	}
2859 
2860 	auto find = statMappings.find(str);
2861 	if ( find != statMappings.end() )
2862 	{
2863 		return (*find).second;
2864 	}
2865 	return nullptr;
2866 }
2867 
queryAllStats()2868 void EOSFuncs::queryAllStats()
2869 {
2870 	if ( !statMappings.size() )
2871 	{
2872 		for ( Uint32 i = 0; i < NUM_STEAM_STATISTICS; ++i )
2873 		{
2874 			statMappings[g_SteamStats[i].m_pchStatName] = &g_SteamStats[i];
2875 		}
2876 	}
2877 
2878 	StatsHandle = EOS_Platform_GetStatsInterface(PlatformHandle);
2879 
2880 	// Query Player Stats
2881 	EOS_Stats_QueryStatsOptions StatsQueryOptions = {};
2882 	StatsQueryOptions.ApiVersion = EOS_STATS_QUERYSTATS_API_LATEST;
2883 	StatsQueryOptions.UserId = CurrentUserInfo.getProductUserIdHandle();
2884 
2885 	// Optional params
2886 	StatsQueryOptions.StartTime = EOS_STATS_TIME_UNDEFINED;
2887 	StatsQueryOptions.EndTime = EOS_STATS_TIME_UNDEFINED;
2888 
2889 	StatsQueryOptions.StatNamesCount = NUM_STEAM_STATISTICS;
2890 	StatsQueryOptions.StatNames = new const char*[NUM_STEAM_STATISTICS];
2891 
2892 	for ( int i = 0; i < NUM_STEAM_STATISTICS; ++i )
2893 	{
2894 		StatsQueryOptions.StatNames[i] = g_SteamStats[i].m_pchStatName;
2895 	}
2896 
2897 	EOS_Stats_QueryStats(StatsHandle, &StatsQueryOptions, nullptr, OnQueryAllStatsCallback);
2898 	delete[] StatsQueryOptions.StatNames;
2899 }
2900 
showFriendsOverlay()2901 void EOSFuncs::showFriendsOverlay()
2902 {
2903 	UIHandle = EOS_Platform_GetUIInterface(PlatformHandle);
2904 	EOS_UI_SetDisplayPreferenceOptions DisplayOptions;
2905 	DisplayOptions.ApiVersion = EOS_UI_SETDISPLAYPREFERENCE_API_LATEST;
2906 	DisplayOptions.NotificationLocation = EOS_UI_ENotificationLocation::EOS_UNL_TopRight;
2907 
2908 	EOS_EResult result = EOS_UI_SetDisplayPreference(UIHandle, &DisplayOptions);
2909 	EOSFuncs::logInfo("showFriendsOverlay: result: %d", static_cast<int>(result));
2910 
2911 	UIHandle = EOS_Platform_GetUIInterface(PlatformHandle);
2912 	EOS_UI_ShowFriendsOptions Options = {};
2913 	Options.ApiVersion = EOS_UI_SHOWFRIENDS_API_LATEST;
2914 	Options.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str());
2915 
2916 	EOS_UI_ShowFriends(UIHandle, &Options, nullptr, ShowFriendsCallback);
2917 }
2918 
ShowFriendsCallback(const EOS_UI_ShowFriendsCallbackInfo * data)2919 void EOS_CALL EOSFuncs::ShowFriendsCallback(const EOS_UI_ShowFriendsCallbackInfo* data)
2920 {
2921 	if ( data )
2922 	{
2923 		EOSFuncs::logInfo("ShowFriendsCallback: result: %d", static_cast<int>(data->ResultCode));
2924 	}
2925 }
2926 
initAuth(std::string hostname,std::string tokenName)2927 bool EOSFuncs::initAuth(std::string hostname, std::string tokenName)
2928 {
2929 	AuthHandle = EOS_Platform_GetAuthInterface(PlatformHandle);
2930 	ConnectHandle = EOS_Platform_GetConnectInterface(PlatformHandle);
2931 
2932 	EOS_Auth_Credentials Credentials = {};
2933 	Credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST;
2934 	if ( hostname.compare("") == 0 )
2935 	{
2936 		Credentials.Id = nullptr;
2937 	}
2938 	else
2939 	{
2940 		Credentials.Id = hostname.c_str();
2941 	}
2942 	if ( tokenName.compare("") == 0 )
2943 	{
2944 		Credentials.Token = nullptr;
2945 	}
2946 	else
2947 	{
2948 		Credentials.Token = tokenName.c_str();
2949 	}
2950 	Credentials.Type = EOS.AccountManager.AuthType;
2951 	if ( Credentials.Type == EOS_ELoginCredentialType::EOS_LCT_Developer )
2952 	{
2953 		EOSFuncs::logInfo("Connecting to \'%s\'...", hostname.c_str());
2954 	}
2955 	else if ( Credentials.Type == EOS_ELoginCredentialType::EOS_LCT_ExchangeCode )
2956 	{
2957 		EOSFuncs::logInfo("Connecting via exchange token...");
2958 	}
2959 
2960 	EOS_Auth_LoginOptions LoginOptions = {};
2961 	LoginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST;
2962 	LoginOptions.ScopeFlags = static_cast<EOS_EAuthScopeFlags>(EOS_EAuthScopeFlags::EOS_AS_BasicProfile | EOS_EAuthScopeFlags::EOS_AS_FriendsList | EOS_EAuthScopeFlags::EOS_AS_Presence);
2963 	LoginOptions.Credentials = &Credentials;
2964 
2965 	EOS_Auth_Login(AuthHandle, &LoginOptions, NULL, AuthLoginCompleteCallback);
2966 
2967 	AddConnectAuthExpirationNotification();
2968 
2969 	EOS.AccountManager.waitingForCallback = true;
2970 	Uint32 startAuthTicks = SDL_GetTicks();
2971 	Uint32 currentAuthTicks = startAuthTicks;
2972 #ifndef STEAMWORKS
2973 	while ( EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_NotConfigured )
2974 	{
2975 #ifdef APPLE
2976 		SDL_Event event;
2977 		while ( SDL_PollEvent(&event) != 0 )
2978 		{
2979 			//Makes Mac work because Apple had to do it different.
2980 		}
2981 #endif
2982 		EOS_Platform_Tick(PlatformHandle);
2983 		SDL_Delay(50);
2984 		currentAuthTicks = SDL_GetTicks();
2985 		if ( currentAuthTicks - startAuthTicks >= 3000 ) // spin the wheels for 3 seconds
2986 		{
2987 			break;
2988 		}
2989 	}
2990 #endif // !STEAMWORKS
2991 
2992 	bool achResult = initAchievements();
2993 	assert(achResult == true);
2994 	return true;
2995 }
2996 
sortResults()2997 void EOSFuncs::LobbySearchResults_t::sortResults()
2998 {
2999 	resultsSortedForDisplay.clear();
3000 	if ( results.empty() )
3001 	{
3002 		return;
3003 	}
3004 
3005 	int index = 0;
3006 	for ( auto it = results.begin(); it != results.end(); ++it )
3007 	{
3008 		resultsSortedForDisplay.push_back(std::make_pair(EOSFuncs::LobbyData_t::LobbyAttributes_t((*it).LobbyAttributes), index));
3009 		++index;
3010 	}
3011 	std::sort(resultsSortedForDisplay.begin(), resultsSortedForDisplay.end(),
3012 		[](const std::pair<EOSFuncs::LobbyData_t::LobbyAttributes_t, int>& lhs, const std::pair<EOSFuncs::LobbyData_t::LobbyAttributes_t, int>& rhs)
3013 		{
3014 			if ( lhs.first.gameCurrentLevel == -1 && rhs.first.gameCurrentLevel == -1 )
3015 			{
3016 				return (lhs.first.lobbyCreationTime > rhs.first.lobbyCreationTime);
3017 			}
3018 			return lhs.first.gameCurrentLevel < rhs.first.gameCurrentLevel;
3019 		}
3020 	);
3021 }
3022 
buttonRetryAuthorisation()3023 void buttonRetryAuthorisation()
3024 {
3025 	EOS.AccountManager.AccountAuthenticationStatus = EOS_EResult::EOS_NotConfigured;
3026 	EOS.initAuth();
3027 
3028 	UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT);
3029 	if ( n )
3030 	{
3031 		n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); // unset
3032 		n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3033 		n->showMainCard();
3034 		n->setDisplayedText(n->getMainText());
3035 		n->updateCardEvent(true, false);
3036 	}
3037 }
3038 
handleLogin()3039 void EOSFuncs::Accounts_t::handleLogin()
3040 {
3041 #ifdef STEAMWORKS
3042 	return;
3043 #endif
3044 	if ( !initPopupWindow && popupType == POPUP_TOAST )
3045 	{
3046 		if ( !UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT) )
3047 		{
3048 			UIToastNotification* n = UIToastNotificationManager.addNotification(nullptr);
3049 			n->setHeaderText(std::string("Account Status"));
3050 			n->setMainText(std::string("Logging in..."));
3051 			n->setSecondaryText(std::string("An error has occurred!"));
3052 			n->setActionText(std::string("Retry"));
3053 			n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); // unset
3054 			n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3055 			n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
3056 			n->cardType = UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT;
3057 			n->buttonAction = &buttonRetryAuthorisation;
3058 			n->setIdleSeconds(5);
3059 		}
3060 		initPopupWindow = true;
3061 	}
3062 
3063 	if ( !waitingForCallback && (AccountAuthenticationStatus == EOS_EResult::EOS_Success || AccountAuthenticationCompleted == EOS_EResult::EOS_Success) )
3064 	{
3065 		firstTimeSetupCompleted = true;
3066 		if ( AccountAuthenticationCompleted != EOS_EResult::EOS_Success )
3067 		{
3068 			if ( popupType == POPUP_TOAST )
3069 			{
3070 				UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT);
3071 				if ( n )
3072 				{
3073 					n->showMainCard();
3074 					n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3075 					n->setSecondaryText(std::string("Logged in successfully!"));
3076 					n->updateCardEvent(false, true);
3077 					n->setIdleSeconds(5);
3078 				}
3079 			}
3080 			AccountAuthenticationCompleted = EOS_EResult::EOS_Success;
3081 		}
3082 		return;
3083 	}
3084 	AccountAuthenticationCompleted = EOS_EResult::EOS_NotConfigured;
3085 
3086 	// handle errors below...
3087 	if ( initPopupWindow && popupType == POPUP_TOAST )
3088 	{
3089 		if ( !waitingForCallback )
3090 		{
3091 			if ( AccountAuthenticationStatus != EOS_EResult::EOS_NotConfigured )
3092 			{
3093 				UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_EOS_ACCOUNT);
3094 				// update the status here.
3095 				if ( n )
3096 				{
3097 					n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
3098 					n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3099 					char buf[128] = "";
3100 					snprintf(buf, 128 - 1, "Login has failed.\nError code: %d", static_cast<int>(AccountAuthenticationStatus));
3101 					n->showMainCard();
3102 					n->setSecondaryText(std::string(buf));
3103 					n->updateCardEvent(false, true);
3104 					n->setIdleSeconds(10);
3105 				}
3106 			}
3107 			AccountAuthenticationStatus = EOS_EResult::EOS_NotConfigured;
3108 		}
3109 	}
3110 
3111 	//if ( popupType == POPUP_FULL )
3112 	//{
3113 	//	// close this popup.
3114 	//	buttonCloseSubwindow(nullptr);
3115 	//	list_FreeAll(&button_l);
3116 	//	deleteallbuttons = true;
3117 	//}
3118 	/*if ( !initPopupWindow && firstTimeSetupCompleted )
3119 	{
3120 		popupType = POPUP_TOAST;
3121 	}*/
3122 
3123 	//if ( popupType == POPUP_FULL )
3124 	//{
3125 	//	if ( !initPopupWindow )
3126 	//	{
3127 	//		createLoginDialogue();
3128 	//	}
3129 	//}
3130 	//drawDialogue();
3131 }
3132 
createNotification()3133 void EOSFuncs::CrossplayAccounts_t::createNotification()
3134 {
3135 	if ( !UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT) )
3136 	{
3137 		UIToastNotification* n = UIToastNotificationManager.addNotification(nullptr);
3138 		n->setHeaderText(std::string("Crossplay Status"));
3139 		n->setMainText(std::string("Initializing..."));
3140 		n->setSecondaryText(std::string("An error has occurred!"));
3141 		n->setActionText(std::string("Retry"));
3142 		n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON); // unset
3143 		n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3144 		n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
3145 		n->cardType = UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT;
3146 		n->setIdleSeconds(5);
3147 	}
3148 }
3149 
handleLogin()3150 void EOSFuncs::CrossplayAccounts_t::handleLogin()
3151 {
3152 #ifndef STEAMWORKS
3153 	return;
3154 #endif // !STEAMWORKS
3155 
3156 	drawDialogue();
3157 
3158 	if ( logOut )
3159 	{
3160 		if ( awaitingAppTicketResponse || awaitingConnectCallback || awaitingCreateUserCallback )
3161 		{
3162 			EOSFuncs::logInfo("Crossplay logout pending callbacks...");
3163 			if ( awaitingAppTicketResponse )
3164 			{
3165 				EOSFuncs::logInfo("Callback AppTicket still pending...");
3166 			}
3167 			if ( awaitingConnectCallback )
3168 			{
3169 				EOSFuncs::logInfo("Callback Connect still pending...");
3170 			}
3171 			if ( awaitingCreateUserCallback )
3172 			{
3173 				EOSFuncs::logInfo("Callback CreateUser still pending...");
3174 			}
3175 			return;
3176 		}
3177 		EOSFuncs::logInfo("Crossplay logout request received.");
3178 		EOS.UnsubscribeFromConnectionRequests();
3179 		EOS.CurrentUserInfo.setProductUserIdHandle(nullptr);
3180 		EOS.CurrentUserInfo.bUserLoggedIn = false;
3181 
3182 		resetOnFailure();
3183 		for ( auto it = UIToastNotificationManager.allNotifications.begin(); it != UIToastNotificationManager.allNotifications.end(); )
3184 		{
3185 			if ( (*it).cardType == UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT )
3186 			{
3187 				it = UIToastNotificationManager.allNotifications.erase(it);
3188 				continue;
3189 			}
3190 			++it;
3191 		}
3192 		logOut = false;
3193 		EOSFuncs::logInfo("Crossplay logout completed.");
3194 		return;
3195 	}
3196 
3197 	bool initLogin = false;
3198 	if ( autologin )
3199 	{
3200 		initLogin = true;
3201 		autologin = false;
3202 	}
3203 	if ( trySetupFromSettingsMenu )
3204 	{
3205 		initLogin = true;
3206 		trySetupFromSettingsMenu = false;
3207 		if ( subwindow )
3208 		{
3209 			buttonCloseSubwindow(nullptr);
3210 		}
3211 	}
3212 
3213 	if ( initLogin )
3214 	{
3215 #ifdef STEAMWORKS
3216 		cpp_SteamMatchmaking_RequestAppTicket();
3217 #endif // STEAMWORKS
3218 
3219 		createNotification();
3220 		awaitingAppTicketResponse = true;
3221 		EOSFuncs::logInfo("Crossplay login request started...");
3222 		return;
3223 	}
3224 
3225 	if ( awaitingAppTicketResponse )
3226 	{
3227 		return;
3228 	}
3229 	if ( awaitingConnectCallback )
3230 	{
3231 		return;
3232 	}
3233 	if ( connectLoginStatus == EOS_EResult::EOS_Success )
3234 	{
3235 		if ( connectLoginCompleted != EOS_EResult::EOS_Success )
3236 		{
3237 			connectLoginCompleted = EOS_EResult::EOS_Success;
3238 			UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT);
3239 			if ( n )
3240 			{
3241 				n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
3242 				n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3243 				n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_CLOSE);
3244 				n->showMainCard();
3245 				n->setMainText(std::string("Steam account linked.\nCrossplay enabled."));
3246 				n->updateCardEvent(true, false);
3247 				n->setIdleSeconds(5);
3248 			}
3249 			LobbyHandler.crossplayEnabled = true;
3250 			LobbyHandler.settings_crossplayEnabled = true;
3251 		}
3252 		return;
3253 	}
3254 
3255 	if ( connectLoginCompleted == EOS_EResult::EOS_Success )
3256 	{
3257 		return;
3258 	}
3259 
3260 	if ( connectLoginStatus != EOS_EResult::EOS_NotConfigured )
3261 	{
3262 		if ( connectLoginStatus == EOS_EResult::EOS_InvalidUser )
3263 		{
3264 			UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT);
3265 			if ( n )
3266 			{
3267 				n->actionFlags &= ~(UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
3268 				n->showMainCard();
3269 				n->setSecondaryText(std::string("New Steam user.\nAccept EULA to proceed."));
3270 				n->updateCardEvent(false, true);
3271 				n->setIdleSeconds(10);
3272 			}
3273 			EOSFuncs::logInfo("New Steam user, awaiting user response.");
3274 			createDialogue();
3275 		}
3276 		else
3277 		{
3278 			UIToastNotification* n = UIToastNotificationManager.getNotificationSingle(UIToastNotification::CardType::UI_CARD_CROSSPLAY_ACCOUNT);
3279 			// update the status here.
3280 			if ( n )
3281 			{
3282 				n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_ACTION_BUTTON);
3283 				n->actionFlags |= (UIToastNotification::ActionFlags::UI_NOTIFICATION_AUTO_HIDE);
3284 				char buf[128] = "";
3285 				snprintf(buf, 128 - 1, "Setup has failed.\nError code: %d", static_cast<int>(connectLoginStatus));
3286 				n->showMainCard();
3287 				n->setSecondaryText(std::string(buf));
3288 				n->updateCardEvent(false, true);
3289 				n->buttonAction = &EOSFuncs::CrossplayAccounts_t::retryCrossplaySetupOnFailure;
3290 				n->setIdleSeconds(10);
3291 			}
3292 			EOSFuncs::logError("Crossplay setup has failed. Error code: %d", static_cast<int>(connectLoginStatus));
3293 			resetOnFailure();
3294 		}
3295 		connectLoginStatus = EOS_EResult::EOS_NotConfigured;
3296 	}
3297 }
3298 
retryCrossplaySetupOnFailure()3299 void EOSFuncs::CrossplayAccounts_t::retryCrossplaySetupOnFailure()
3300 {
3301 	EOS.CrossplayAccountManager.trySetupFromSettingsMenu = true;
3302 	EOS.CrossplayAccountManager.connectLoginCompleted = EOS_EResult::EOS_NotConfigured;
3303 	EOS.CrossplayAccountManager.connectLoginStatus = EOS_EResult::EOS_NotConfigured;
3304 }
3305 
resetOnFailure()3306 void EOSFuncs::CrossplayAccounts_t::resetOnFailure()
3307 {
3308 	LobbyHandler.settings_crossplayEnabled = false;
3309 	LobbyHandler.crossplayEnabled = false;
3310 	connectLoginCompleted = EOS_EResult::EOS_NotConfigured;
3311 	connectLoginStatus = EOS_EResult::EOS_NotConfigured;
3312 	continuanceToken = nullptr;
3313 }
3314 
buttonAcceptCrossplaySetup(button_t * my)3315 void buttonAcceptCrossplaySetup(button_t* my)
3316 {
3317 	EOS.CrossplayAccountManager.acceptedEula = true;
3318 	EOS.CrossplayAccountManager.closePrompt();
3319 	if ( EOS.CrossplayAccountManager.continuanceToken )
3320 	{
3321 		EOS_Connect_CreateUserOptions CreateUserOptions;
3322 		CreateUserOptions.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST;
3323 		CreateUserOptions.ContinuanceToken = EOS.CrossplayAccountManager.continuanceToken;
3324 
3325 		EOS.ConnectHandle = EOS_Platform_GetConnectInterface(EOS.PlatformHandle);
3326 		EOS_Connect_CreateUser(EOS.ConnectHandle, &CreateUserOptions, nullptr, EOSFuncs::OnCreateUserCrossplayCallback);
3327 
3328 		EOS.CrossplayAccountManager.continuanceToken = nullptr;
3329 	}
3330 	else
3331 	{
3332 		EOS.CrossplayAccountManager.resetOnFailure();
3333 	}
3334 	buttonCloseSubwindow(nullptr);
3335 	EOSFuncs::logInfo("Crossplay account link has been accepted by user");
3336 }
3337 
buttonDenyCrossplaySetup(button_t * my)3338 void buttonDenyCrossplaySetup(button_t* my)
3339 {
3340 	EOS.CrossplayAccountManager.logOut = true;
3341 	EOS.CrossplayAccountManager.closePrompt();
3342 	EOS.CrossplayAccountManager.resetOnFailure();
3343 	buttonCloseSubwindow(nullptr);
3344 	EOSFuncs::logInfo("Crossplay account link has been denied by user");
3345 }
3346 
buttonViewPrivacyPolicy(button_t * my)3347 void buttonViewPrivacyPolicy(button_t* my)
3348 {
3349 	openURLTryWithOverlay("http://www.baronygame.com/privacypolicy.html");
3350 }
3351 
drawDialogue()3352 void EOSFuncs::CrossplayAccounts_t::drawDialogue()
3353 {
3354 	if ( getPromptStatus() == PROMPT_CLOSED )
3355 	{
3356 		return;
3357 	}
3358 
3359 	if ( getPromptStatus() == PROMPT_SETUP )
3360 	{
3361 		ttfPrintTextFormattedColor(ttf12, subx1 + 12, suby1 + 8, uint32ColorYellow(*mainsurface), "%s", language[3979]);
3362 	}
3363 	else if ( getPromptStatus() == PROMPT_ABOUT )
3364 	{
3365 		ttfPrintTextFormattedColor(ttf12, subx1 + 12, suby1 + 8, uint32ColorYellow(*mainsurface), "%s", language[3981]);
3366 	}
3367 }
3368 
buttonReopenCrossplaySetup(button_t * my)3369 void buttonReopenCrossplaySetup(button_t* my)
3370 {
3371 	EOS.CrossplayAccountManager.createDialogue();
3372 }
3373 
buttonAboutCrossplay(button_t * my)3374 void buttonAboutCrossplay(button_t* my)
3375 {
3376 	// close current window
3377 	buttonCloseSubwindow(NULL);
3378 	list_FreeAll(&button_l);
3379 	deleteallbuttons = true;
3380 
3381 	EOS.CrossplayAccountManager.openPromptAbout();
3382 
3383 	// create new window
3384 	subwindow = 1;
3385 	subx1 = xres / 2 - 356;
3386 	subx2 = xres / 2 + 356;
3387 	suby1 = yres / 2 - 133;
3388 	suby2 = yres / 2 + 133;
3389 	strcpy(subtext, language[3980]);
3390 
3391 	button_t* button;
3392 	// privacy policy button
3393 	button = newButton();
3394 	strcpy(button->label, language[3978]);
3395 	button->sizex = strlen(language[3978]) * 12 + 8;
3396 	button->sizey = 20;
3397 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
3398 	button->y = suby2 - 48;
3399 	button->visible = 1;
3400 	button->focused = 1;
3401 	button->action = &buttonViewPrivacyPolicy;
3402 
3403 	// return to previous button
3404 	button = newButton();
3405 	strcpy(button->label, language[3982]);
3406 	button->sizex = strlen(language[3982]) * 12 + 4;
3407 	button->sizey = 20;
3408 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
3409 	button->y = suby2 - 24;
3410 	button->visible = 1;
3411 	button->focused = 1;
3412 	button->action = &buttonReopenCrossplaySetup;
3413 }
3414 
createDialogue()3415 void EOSFuncs::CrossplayAccounts_t::createDialogue()
3416 {
3417 	// close current window
3418 	buttonCloseSubwindow(NULL);
3419 	list_FreeAll(&button_l);
3420 	deleteallbuttons = true;
3421 
3422 	openPromptSetup();
3423 
3424 	// create new window
3425 	subwindow = 1;
3426 	subx1 = xres / 2 - 326;
3427 	subx2 = xres / 2 + 326;
3428 	suby1 = yres / 2 - 116;
3429 	suby2 = yres / 2 + 116;
3430 	strcpy(subtext, language[3975]);
3431 
3432 	button_t* button;
3433 	// accept button
3434 	button = newButton();
3435 	strcpy(button->label, language[3976]);
3436 	button->sizex = strlen(language[3976]) * 12 + 8;
3437 	button->sizey = 20;
3438 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
3439 	button->y = suby2 - 48;
3440 	button->visible = 1;
3441 	button->focused = 1;
3442 	button->action = &buttonAcceptCrossplaySetup;
3443 
3444 	// deny button
3445 	button = newButton();
3446 	strcpy(button->label, language[3977]);
3447 	button->sizex = strlen(language[3977]) * 12 + 4;
3448 	button->sizey = 20;
3449 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
3450 	button->y = suby2 - 24;
3451 	button->visible = 1;
3452 	button->focused = 1;
3453 	button->action = &buttonDenyCrossplaySetup;
3454 
3455 	// about crossplay button
3456 	button = newButton();
3457 	strcpy(button->label, language[3983]);
3458 	button->sizex = strlen(language[3983]) * 12 + 8;
3459 	button->sizey = 20;
3460 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
3461 	button->y = suby2 - 48 - 70;
3462 	button->visible = 1;
3463 	button->focused = 1;
3464 	button->action = &buttonAboutCrossplay;
3465 }
3466 
getLobbyCodeFromGameKey(Uint32 key)3467 std::string EOSFuncs::getLobbyCodeFromGameKey(Uint32 key)
3468 {
3469 	const char allChars[37] = "0123456789abcdefghijklmnopqrstuvwxyz";
3470 	std::string code = "";
3471 	while ( key != 0 )
3472 	{
3473 		code += (allChars[key % 36]);
3474 		key /= 36;
3475 	}
3476 	while ( code.size() < 4 )
3477 	{
3478 		code += '0';
3479 	}
3480 	return code;
3481 }
getGameKeyFromLobbyCode(std::string & code)3482 Uint32 EOSFuncs::getGameKeyFromLobbyCode(std::string& code)
3483 {
3484 	const char allChars[37] = "0123456789abcdefghijklmnopqrstuvwxyz";
3485 	Uint32 result = 0;
3486 	Uint32 bit = 0;
3487 	for ( int i = 0; i < code.size(); ++i )
3488 	{
3489 		if ( code[i] >= 'A' && code[i] <= 'Z' )
3490 		{
3491 			code[i] = 'a' + (code[i] - 'A');
3492 		}
3493 
3494 		if ( code[i] >= '0' && code[i] <= '9' )
3495 		{
3496 			result += static_cast<Uint32>(code[i] - '0') * static_cast<Uint32>(pow(36, bit));
3497 			++bit;
3498 		}
3499 		else if ( code[i] >= 'a' && code[i] <= 'z' )
3500 		{
3501 			result += (static_cast<Uint32>(code[i] - 'a') + 10) * static_cast<Uint32>(pow(36, bit));
3502 			++bit;
3503 		}
3504 	}
3505 	return result;
3506 }
3507 
queryDLCOwnership()3508 void EOSFuncs::queryDLCOwnership()
3509 {
3510 	EcomHandle = EOS_Platform_GetEcomInterface(PlatformHandle);
3511 
3512 	EOS_Ecom_QueryEntitlementsOptions options = {};
3513 	options.ApiVersion = EOS_ECOM_QUERYENTITLEMENTS_API_LATEST;
3514 	options.bIncludeRedeemed = true;
3515 	std::vector<EOS_Ecom_EntitlementName> entitlements;
3516 	entitlements.push_back("fced51d547714291869b8847fdd770e8");
3517 	entitlements.push_back("7ea3754f8bfa4069938fd0bee3e7197b");
3518 
3519 	options.EntitlementNames = entitlements.data();
3520 	options.EntitlementNameCount = entitlements.size();
3521 	options.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(CurrentUserInfo.epicAccountId.c_str());
3522 
3523 	EOS_Ecom_QueryEntitlements(EcomHandle, &options, nullptr, OnEcomQueryEntitlementsCallback);
3524 }
3525 
OnEcomQueryEntitlementsCallback(const EOS_Ecom_QueryEntitlementsCallbackInfo * data)3526 void EOS_CALL EOSFuncs::OnEcomQueryEntitlementsCallback(const EOS_Ecom_QueryEntitlementsCallbackInfo* data)
3527 {
3528 	if ( !data )
3529 	{
3530 		EOSFuncs::logError("OnEcomQueryEntitlementsCallback: null data");
3531 		return;
3532 	}
3533 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
3534 	{
3535 		EOSFuncs::logInfo("OnEcomQueryEntitlementsCallback: callback success");
3536 
3537 		EOS.EcomHandle = EOS_Platform_GetEcomInterface(EOS.PlatformHandle);
3538 		EOS_Ecom_GetEntitlementsCountOptions countOptions = {};
3539 		countOptions.ApiVersion = EOS_ECOM_GETENTITLEMENTSCOUNT_API_LATEST;
3540 		countOptions.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str());
3541 
3542 		Uint32 numEntitlements = EOS_Ecom_GetEntitlementsCount(EOS.EcomHandle, &countOptions);
3543 		//EOSFuncs::logInfo("OnEcomQueryEntitlementsCallback: %d entitlements", numEntitlements);
3544 		for ( int i = 0; i < numEntitlements; ++i )
3545 		{
3546 			EOS_Ecom_CopyEntitlementByIndexOptions copyOptions;
3547 			copyOptions.ApiVersion = EOS_ECOM_COPYENTITLEMENTBYINDEX_API_LATEST;
3548 			copyOptions.EntitlementIndex = i;
3549 			copyOptions.LocalUserId = EOSFuncs::Helpers_t::epicIdFromString(EOS.CurrentUserInfo.epicAccountId.c_str());
3550 
3551 			EOS_Ecom_Entitlement* e = nullptr;
3552 			EOS_EResult result = EOS_Ecom_CopyEntitlementByIndex(EOS.EcomHandle, &copyOptions, &e);
3553 			//EOSFuncs::logInfo("%d:", static_cast<int>(result));
3554 			if ( (result == EOS_EResult::EOS_Success || result == EOS_EResult::EOS_Ecom_EntitlementStale) && e )
3555 			{
3556 				std::string id = e->EntitlementName;
3557 				if ( id.compare("fced51d547714291869b8847fdd770e8") == 0 )
3558 				{
3559 					enabledDLCPack1 = true;
3560 					EOSFuncs::logInfo("Myths & Outcasts DLC Enabled");
3561 				}
3562 				else if ( id.compare("7ea3754f8bfa4069938fd0bee3e7197b") == 0 )
3563 				{
3564 					enabledDLCPack2 = true;
3565 					EOSFuncs::logInfo("Legends & Pariahs DLC Enabled");
3566 				}
3567 				//EOSFuncs::logInfo("Index: %d | Id %s: | Entitlement Name: %s | CatalogItemId: %s | Redeemed: %d", i, e->EntitlementId, e->EntitlementName, e->CatalogItemId, (e->bRedeemed == EOS_TRUE) ? 1 : 0);
3568 			}
3569 			EOS_Ecom_Entitlement_Release(e);
3570 
3571 		}
3572 	}
3573 	else
3574 	{
3575 		EOSFuncs::logError("OnEcomQueryEntitlementsCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
3576 	}
3577 }
3578 
OnEcomQueryOwnershipCallback(const EOS_Ecom_QueryOwnershipCallbackInfo * data)3579 void EOS_CALL EOSFuncs::OnEcomQueryOwnershipCallback(const EOS_Ecom_QueryOwnershipCallbackInfo* data)
3580 {
3581 	if ( !data )
3582 	{
3583 		EOSFuncs::logError("OnEcomQueryOwnershipCallback: null data");
3584 		return;
3585 	}
3586 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
3587 	{
3588 		for ( int i = 0; i < data->ItemOwnershipCount; ++i )
3589 		{
3590 			EOSFuncs::logInfo("OnEcomQueryOwnershipCallback: Ownership status: %d, %d", static_cast<int>(data->ItemOwnership[i].OwnershipStatus), data->ItemOwnershipCount);
3591 		}
3592 	}
3593 	else
3594 	{
3595 		EOSFuncs::logError("OnEcomQueryOwnershipCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
3596 	}
3597 }
3598 
OnQueryGlobalStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo * data)3599 static void EOS_CALL OnQueryGlobalStatsCallback(const EOS_Stats_OnQueryStatsCompleteCallbackInfo* data)
3600 {
3601 	if ( !data )
3602 	{
3603 		EOSFuncs::logError("OnQueryGlobalStatsCallback: null data");
3604 		return;
3605 	}
3606 	else if ( data->ResultCode == EOS_EResult::EOS_Success )
3607 	{
3608 		EOS_Stats_GetStatCountOptions StatCountOptions = {};
3609 		StatCountOptions.ApiVersion = EOS_STATS_GETSTATCOUNT_API_LATEST;
3610 		StatCountOptions.UserId = EOS.StatGlobalManager.getProductUserIdHandle();
3611 
3612 		Uint32 numStats = EOS_Stats_GetStatsCount(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle),
3613 			&StatCountOptions);
3614 
3615 		EOSFuncs::logInfo("OnQueryGlobalStatsCallback: read %d stats", numStats);
3616 
3617 		EOS_Stats_CopyStatByIndexOptions CopyByIndexOptions = {};
3618 		CopyByIndexOptions.ApiVersion = EOS_STATS_COPYSTATBYINDEX_API_LATEST;
3619 		CopyByIndexOptions.UserId = EOS.StatGlobalManager.getProductUserIdHandle();
3620 
3621 		EOS_Stats_Stat* copyStat = NULL;
3622 
3623 		for ( CopyByIndexOptions.StatIndex = 0; CopyByIndexOptions.StatIndex < numStats; ++CopyByIndexOptions.StatIndex )
3624 		{
3625 			EOS_EResult result = EOS_Stats_CopyStatByIndex(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle),
3626 				&CopyByIndexOptions, &copyStat);
3627 			if ( result == EOS_EResult::EOS_Success && copyStat )
3628 			{
3629 				if ( !strcmp(copyStat->Name, "STAT_GLOBAL_DISABLE") )
3630 				{
3631 					if ( copyStat->Value == 1 )
3632 					{
3633 						//EOSFuncs::logInfo("OnQueryGlobalStatsCallback: disabled");
3634 						EOS.StatGlobalManager.bIsDisabled = true;
3635 					}
3636 				}
3637 				else if ( !strcmp(copyStat->Name, "STAT_GLOBAL_PROMO") )
3638 				{
3639 					if ( copyStat->Value == 1 )
3640 					{
3641 						EOS.StatGlobalManager.bPromoEnabled = true;
3642 						//EOSFuncs::logInfo("OnQueryGlobalStatsCallback: received");
3643 					}
3644 				}
3645 
3646 				//EOSFuncs::logInfo("OnQueryGlobalStatsCallback: stat %s | %d", copyStat->Name, copyStat->Value);
3647 				EOS_Stats_Stat_Release(copyStat);
3648 			}
3649 		}
3650 	}
3651 	else
3652 	{
3653 		EOSFuncs::logError("OnQueryGlobalStatsCallback: Callback failure: %d", static_cast<int>(data->ResultCode));
3654 	}
3655 }
3656 
init()3657 void EOSFuncs::StatGlobal_t::init()
3658 {
3659 	bIsInit = true;
3660 	bIsDisabled = false;
3661 	productUserId = EOS_ProductUserId_FromString(BUILD_ENV_GSE);
3662 }
3663 
queryGlobalStatUser()3664 void EOSFuncs::StatGlobal_t::queryGlobalStatUser()
3665 {
3666 	init();
3667 
3668 	// Query Player Stats
3669 	EOS_Stats_QueryStatsOptions StatsQueryOptions = {};
3670 	StatsQueryOptions.ApiVersion = EOS_STATS_QUERYSTATS_API_LATEST;
3671 	StatsQueryOptions.UserId = getProductUserIdHandle();
3672 
3673 	// Optional params
3674 	StatsQueryOptions.StartTime = EOS_STATS_TIME_UNDEFINED;
3675 	StatsQueryOptions.EndTime = EOS_STATS_TIME_UNDEFINED;
3676 
3677 	StatsQueryOptions.StatNamesCount = NUM_GLOBAL_STEAM_STATISTICS;
3678 	StatsQueryOptions.StatNames = new const char*[NUM_GLOBAL_STEAM_STATISTICS];
3679 
3680 	for ( int i = 0; i < NUM_GLOBAL_STEAM_STATISTICS; ++i )
3681 	{
3682 		StatsQueryOptions.StatNames[i] = g_SteamGlobalStats[i].m_pchStatName;
3683 	}
3684 
3685 	EOS_Stats_QueryStats(EOS_Platform_GetStatsInterface(EOS.ServerPlatformHandle),
3686 		&StatsQueryOptions, nullptr, OnQueryGlobalStatsCallback);
3687 	delete[] StatsQueryOptions.StatNames;
3688 }
3689 
3690 #endif //USE_EOS
3691 
3692 
3693 //void EOSFuncs::Accounts_t::createLoginDialogue()
3694 //{
3695 //	if ( initPopupWindow )
3696 //	{
3697 //		return;
3698 //	}
3699 //	initPopupWindow = true;
3700 //	popupInitTicks = ticks;
3701 //	popupCurrentTicks = ticks;
3702 //
3703 //	if ( !loginBanner )
3704 //	{
3705 //		loginBanner = loadImage("images/system/title.png");
3706 //	}
3707 //
3708 //	// close current window
3709 //	buttonCloseSubwindow(NULL);
3710 //	list_FreeAll(&button_l);
3711 //	deleteallbuttons = true;
3712 //
3713 //	// create new window
3714 //	subwindow = 1;
3715 //	subx1 = xres / 2 - ((loginBanner->w / 2)+ 16);
3716 //	subx2 = xres / 2 + ((loginBanner->w / 2) + 16);
3717 //	suby1 = yres / 2 - ((loginBanner->h / 2) + 64);
3718 //	suby2 = yres / 2 + ((loginBanner->h / 2) + 64);
3719 //	strcpy(subtext, "");
3720 //
3721 //	// close button
3722 //	button_t* button;
3723 //	// retry button
3724 //	button = newButton();
3725 //	strcpy(button->label, "Retry login");
3726 //	button->x = subx1 + 4;
3727 //	button->y = suby2 - 24;
3728 //	button->sizex = strlen("Retry login") * 12 + 8;
3729 //	button->sizey = 20;
3730 //	button->visible = 1;
3731 //	button->focused = 1;
3732 //	button->action = &buttonRetryAuthorisation;
3733 //
3734 //	// qtd button
3735 //	button = newButton();
3736 //	strcpy(button->label, "Quit to desktop");
3737 //	button->sizex = strlen("Quit to desktop") * 12 + 4;
3738 //	button->sizey = 20;
3739 //	button->x = subx2 - button->sizex - 8;
3740 //	button->y = suby2 - 24;
3741 //	button->visible = 1;
3742 //	button->focused = 1;
3743 //	button->action = &buttonQuitConfirm;
3744 //}
3745 //
3746 //void EOSFuncs::Accounts_t::drawDialogue()
3747 //{
3748 //	Uint32 oldTicks = popupCurrentTicks;
3749 //	popupCurrentTicks = ticks;
3750 //
3751 //	if ( fadeout )
3752 //	{
3753 //		return;
3754 //	}
3755 //
3756 //	int centerWindowX = subx1 + (subx2 - subx1) / 2;
3757 //	if ( loginBanner )
3758 //	{
3759 //		SDL_Rect pos;
3760 //		pos.x = centerWindowX - loginBanner->w / 2;
3761 //		pos.y = suby1 + 4;
3762 //		pos.w = loginBanner->w;
3763 //		pos.h = loginBanner->h;
3764 //		drawImage(loginBanner, nullptr, &pos);
3765 //	}
3766 //
3767 //	ttfPrintTextFormatted(ttf12, centerWindowX - strlen(language[3936]) * TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 9, language[3936]);
3768 //
3769 //	char messageBuffer[512] = "";
3770 //	strcpy(messageBuffer, language[3937]);
3771 //	if ( popupCurrentTicks % TICKS_PER_SECOND == 0 && oldTicks != popupCurrentTicks )
3772 //	{
3773 //		++loadingTicks;
3774 //		if ( loadingTicks > 3 )
3775 //		{
3776 //			loadingTicks = 0;
3777 //		}
3778 //	}
3779 //	switch ( loadingTicks )
3780 //	{
3781 //		case 0:
3782 //			break;
3783 //		case 1:
3784 //			strcat(messageBuffer, ".");
3785 //			break;
3786 //		case 2:
3787 //			strcat(messageBuffer, "..");
3788 //			break;
3789 //		case 3:
3790 //			strcat(messageBuffer, "...");
3791 //			break;
3792 //		default:
3793 //			break;
3794 //	}
3795 //
3796 //	if ( waitingForCallback )
3797 //	{
3798 //		ttfPrintTextFormatted(ttf12, centerWindowX - strlen(messageBuffer) * TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 8, messageBuffer);
3799 //	}
3800 //	else
3801 //	{
3802 //		if ( EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_Success )
3803 //		{
3804 //			ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3941]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 8,
3805 //				SDL_MapRGB(mainsurface->format, 0, 255, 0), language[3941]);
3806 //		}
3807 //		else if ( EOS.AccountManager.AccountAuthenticationStatus != EOS_EResult::EOS_NotConfigured )
3808 //		{
3809 //			// general error messages
3810 //			if ( !firstTimeSetupCompleted && EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_Auth_ExchangeCodeNotFound )
3811 //			{
3812 //				if ( !loginCriticalErrorOccurred )
3813 //				{
3814 //					list_FreeAll(&button_l);
3815 //					deleteallbuttons = true;
3816 //
3817 //					// qtd button
3818 //					button_t* button = newButton();
3819 //					strcpy(button->label, "Quit to desktop");
3820 //					button->sizex = strlen("Quit to desktop") * 12 + 4;
3821 //					button->sizey = 20;
3822 //					button->x = subx1 + ((subx2 - subx1) / 2) - (button->sizex / 2);
3823 //					button->y = suby2 - 24;
3824 //					button->visible = 1;
3825 //					button->focused = 1;
3826 //					button->action = &buttonQuitConfirm;
3827 //				}
3828 //				loginCriticalErrorOccurred = true;
3829 //				ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3942]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 8,
3830 //					SDL_MapRGB(mainsurface->format, 255, 128, 0), language[3942]);
3831 //				ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3943]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 7,
3832 //					SDL_MapRGB(mainsurface->format, 255, 128, 0), language[3943]);
3833 //				ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3944]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 5,
3834 //					SDL_MapRGB(mainsurface->format, 255, 255, 0), language[3944]);
3835 //			}
3836 //			else
3837 //			{
3838 //				char errorBuf[512] = "";
3839 //				snprintf(errorBuf, 512 - 1, language[3941], static_cast<int>(EOS.AccountManager.AccountAuthenticationStatus));
3840 //				ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3941]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 8,
3841 //					SDL_MapRGB(mainsurface->format, 255, 0, 0), errorBuf);
3842 //			}
3843 //		}
3844 //	}
3845 //
3846 //	if ( !firstTimeSetupCompleted && EOS.AccountManager.AccountAuthenticationStatus == EOS_EResult::EOS_NotConfigured )
3847 //	{
3848 //		if ( popupCurrentTicks - popupInitTicks >= TICKS_PER_SECOND * 3 )
3849 //		{
3850 //			suby1 = yres / 2 - ((loginBanner->h / 2) + 64);
3851 //			suby2 = yres / 2 + ((loginBanner->h / 2) + 64);
3852 //
3853 //			ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3938]) * TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 6,
3854 //				SDL_MapRGB(mainsurface->format, 255, 255, 0), language[3938]);
3855 //			ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3939]) * TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 5,
3856 //				SDL_MapRGB(mainsurface->format, 255, 255, 0), language[3939]);
3857 //			ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3940]) * TTF12_WIDTH / 2, suby2 + 16 - TTF12_HEIGHT * 4,
3858 //				SDL_MapRGB(mainsurface->format, 255, 255, 0), language[3940]);
3859 //		}
3860 //	}
3861 //}