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, ©Stat);
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, ©Options, &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, ©Stat);
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 //}