Documentation for the Unreal C++ Library
Loading...
Searching...
No Matches
PVROnlineSession.cpp
Go to the documentation of this file.
1// Copyright(c) 2023 PixoVR, LLC. All Rights Reserved.
2
3#include "PVROnlineSession.h"
4#include "Misc/Guid.h"
5#include "OnlineSubsystem.h"
6#include "PVROSubsystem.h"
8#include "OnlineSubsystem.h"
9#include "OnlineSubsystemUtils.h"
10#include "OnlineAsyncTaskManager.h"
11#include "SocketSubsystem.h"
12#include "Interfaces/IPv4/IPv4Address.h"
13#include "JsonObjectConverter.h"
14#include "MultiplayerTypes.h"
15
16#if ENGINE_MAJOR_VERSION >= 5
17#include "Online/OnlineBase.h"
18#include "Online/OnlineSessionNames.h"
19#include "IPAddress.h"
20#endif
21
22#define MAX_PUBLIC_CONNECTIONS 2048
23
24DEFINE_LOG_CATEGORY_STATIC(LogPVROnlineSession, Log, All);
25
26#define Log(pmt, ...) UE_LOG(LogPVROnlineSession, Log, TEXT(pmt), ##__VA_ARGS__)
27#define Warn(pmt, ...) UE_LOG(LogPVROnlineSession, Warning, TEXT(pmt), ##__VA_ARGS__)
28#define Error(pmt, ...) UE_LOG(LogPVROnlineSession, Error, TEXT(pmt), ##__VA_ARGS__)
29#define Fatal(pmt, ...) UE_LOG(LogPVROnlineSession, Fatal, TEXT(pmt), ##__VA_ARGS__)
30
32 HostAddr(nullptr),
33#if ENGINE_MAJOR_VERSION >= 5
34 SessionId(FUniqueNetIdPixoVR::Create(TEXT("INVALID")).Get())
35#else
36 SessionId(TEXT("INVALID"))
37#endif
38{
39}
40
42{
43 // Read the IP from the system
44 bool bCanBindAll=false;
45 HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, bCanBindAll);
46
47 // The below is a workaround for systems that set hostname to a distinct address from 127.0.0.1 on a loopback interface.
48 // See e.g. https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution
49 // and http://serverfault.com/questions/363095/why-does-my-hostname-appear-with-the-address-127-0-1-1-rather-than-127-0-0-1-in
50 // Since we bind to 0.0.0.0, we won't answer on 127.0.1.1, so we need to advertise ourselves as 127.0.0.1 for any other loopback address we may have.
51 uint32 HostIp = 0;
52 HostAddr->GetIp(HostIp); // will return in host order
53 // if this address is on loopback interface, advertise it as 127.0.0.1
54 if ((HostIp & 0xff000000) == 0x7f000000)
55 {
56 HostAddr->SetIp(0x7f000001); // 127.0.0.1
57 }
58
59 // Now set the port that was configured
60 HostAddr->SetPort(GetPortFromNetDriver(Subsystem.GetInstanceName()));
61
62 FGuid OwnerGuid;
63 FPlatformMisc::CreateGuid(OwnerGuid);
64
65#if ENGINE_MAJOR_VERSION >= 5
66 SessionId = FUniqueNetIdPixoVR::Create(OwnerGuid.ToString()).Get();
67#else
68 SessionId = FUniqueNetIdPixoVR(OwnerGuid.ToString());
69#endif
70 Log("Session ID is %s.", *SessionId.ToString());
71}
72
74 Subsystem(InSubsystem),
75 CurrentSessionSearch(nullptr),
76 SessionSearchStartTime(0),
77 SessionSearchEndTime(0)
78{
79 Subsystem->WebSocket.OnWebSocketConnectFailed().AddLambda([&](const FString& ErrorMessage) ->
80 void { CancelFindSessions(); }
81 );
82 Subsystem->WebSocket.OnWebSocketDisconnected().AddLambda([&](int32 StatusCode, const FString& Reason, bool bWasClean) ->
83 void { if (StatusCode != 1006) { CancelFindSessions(); } }
84 );
85 Subsystem->WebSocket.OnWebSocketMessageReceived().AddLambda([&](const FString& Message) ->
86 void { HandleMessageReceived(Message); }
87 );
88}
89
90void FPVROnlineSession::HandleMessageReceived(const FString& InJsonMessage)
91{
92 TSharedPtr<FJsonObject> JsonMessageObject = MakeShareable(new FJsonObject);
93 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(*InJsonMessage);
94
95 if (!FJsonSerializer::Deserialize(Reader, JsonMessageObject))
96 {
97 Log("Failed to deserialize the websocket message.");
98 bool bHasCurrentSearch = CurrentSessionSearch.IsValid() ? (CurrentSessionSearch->SearchState == EOnlineAsyncTaskState::InProgress) : false;
99 TriggerOnFindSessionsCompleteDelegates(false);
100 }
101 else
102 {
103 Log("Successfully deserialized the websocket message.")
104 bool bHasError = JsonMessageObject->GetBoolField("error");
105 if (!bHasError)
106 {
107 Log("Doesn't have an error. Checking to see if we found a match.");
108 FString Message = JsonMessageObject->GetStringField("message");
109
110 if (Message.Equals("match found", ESearchCase::IgnoreCase))
111 {
112 Log("Found a match!");
113 SessionSearchEndTime = FPlatformTime::Seconds();
114 CheckMatchesFound(JsonMessageObject);
116 }
117 }
118 else
119 {
120 FString ErrorMessage = JsonMessageObject->GetStringField("message");
121 Log("Websocket error: %s", *ErrorMessage);
123 }
124 }
125}
126
127void FPVROnlineSession::CheckMatchesFound(TSharedPtr<FJsonObject>& JsonMessageObject)
128{
129 FMatchDetails FoundMatchDetails;
130 TSharedPtr<FJsonObject> MatchDetailsObject = JsonMessageObject->GetObjectField("matchDetails");
131
133 {
134 Error("The current search has been lost.");
135 TriggerOnFindSessionsCompleteDelegates(false);
136 return;
137 }
138
139 if (FJsonObjectConverter::JsonObjectToUStruct<FMatchDetails>(MatchDetailsObject.ToSharedRef(), &FoundMatchDetails))
140 {
141 Log("Parsed the match details.");
142 int32 PingInMs = static_cast<int32>((SessionSearchEndTime - SessionSearchStartTime) * 1000);
143
144 FOnlineSessionSearchResult* SearchResult = new (CurrentSessionSearch->SearchResults) FOnlineSessionSearchResult();
145 SearchResult->PingInMs = PingInMs;
146 FOnlineSession* NewSession = &SearchResult->Session;
147
148 FPVROSessionInfo* SessionInfo = new FPVROSessionInfo();
149 SessionInfo->HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
150#if ENGINE_MAJOR_VERSION >= 5
151 SessionInfo->SessionId = FUniqueNetIdPixoVR::Create(FoundMatchDetails.SessionID).Get();
152#else
153 SessionInfo->SessionId = FUniqueNetIdPixoVR(FoundMatchDetails.SessionID);
154#endif
155 int32 HostAddress = 0;
156 int32 HostPort = 0;
157
158 GetHostAndPort(HostAddress, HostPort, FoundMatchDetails.IPAddress, FoundMatchDetails.Port);
159 SessionInfo->HostAddr->SetIp(HostAddress);
160 SessionInfo->HostAddr->SetPort(HostPort);
161 NewSession->SessionSettings.Set(SETTING_MAPNAME, FoundMatchDetails.MapName);
162 NewSession->OwningUserName = FoundMatchDetails.OwningUserName;
163
164 NewSession->SessionSettings.NumPublicConnections = MAX_PUBLIC_CONNECTIONS;
165 NewSession->SessionSettings.NumPrivateConnections = MAX_PUBLIC_CONNECTIONS;
166 NewSession->NumOpenPublicConnections = MAX_PUBLIC_CONNECTIONS;
167 NewSession->NumOpenPrivateConnections = MAX_PUBLIC_CONNECTIONS;
168
169 NewSession->SessionInfo = MakeShareable(SessionInfo);
170
171 TriggerOnFindSessionsCompleteDelegates(true);
172 }
173 else
174 {
175 TriggerOnFindSessionsCompleteDelegates(false);
176 }
177}
178
179uint32 FPVROnlineSession::CreateInternetSession(int32 HostingPlayerNum, FNamedOnlineSession* Session)
180{
181 check(Session);
182
183 return PVRO_SUCCESS;
184}
185
186uint32 FPVROnlineSession::CreateLANSession(int32 HostingPlayerNum, FNamedOnlineSession* Session)
187{
188 check(Session);
189 uint32 Result = PVRO_SUCCESS;
190
191 if (LANSessionManager.GetBeaconState() != ELanBeaconState::Searching)
192 {
193 if (Sessions.Num() > 0)
194 {
196
197 FOnValidQueryPacketDelegate QueryPacketDelegate = FOnValidQueryPacketDelegate::CreateRaw(this, &FPVROnlineSession::OnValidQueryPacketReceived);
198 FOnPortChangedDelegate PortChangedDelegate = FOnPortChangedDelegate::CreateRaw(this, &FPVROnlineSession::OnSessionListenPortChanged);
199 //TODO: if its a LAN Connection just send port 1 for now, maybe change this...
200 if (!LANSessionManager.Host(QueryPacketDelegate))
201 {
202 Result = PVRO_FAIL;
203
205 }
206
207 Result = PVRO_SUCCESS;
208 }
209 }
210
211 return Result;
212}
213
214bool FPVROnlineSession::CreateSession(int32 HostingPlayerNum, FName SessionName, const FOnlineSessionSettings& NewSessionSettings)
215{
216 uint32 Result = PVRO_FAIL;
217
218 // Check for an existing session
219 FNamedOnlineSession* Session = GetNamedSession(SessionName);
220 if (Session == nullptr)
221 {
222 // Create a new session and deep copy the game settings
223 Session = AddNamedSession(SessionName, NewSessionSettings);
224 check(Session);
225 Session->SessionState = EOnlineSessionState::Creating;
226 Session->HostingPlayerNum = HostingPlayerNum;
227
228 check(Subsystem);
229
230 IOnlineIdentityPtr Identity = Subsystem->GetIdentityInterface();
231 if (Identity.IsValid())
232 {
233 Session->OwningUserId = Identity->GetUniquePlayerId(HostingPlayerNum);
234 Session->OwningUserName = Identity->GetPlayerNickname(HostingPlayerNum);
235 }
236
237 // if did not get a valid one, use just something
238 if (!Session->OwningUserId.IsValid())
239 {
240#if ENGINE_MAJOR_VERSION >= 5
241 Session->OwningUserId = FUniqueNetIdPixoVR::Create(FString::Printf(TEXT("%d"), HostingPlayerNum)).ToSharedPtr();
242#else
243 Session->OwningUserId = MakeShareable(new FUniqueNetIdPixoVR(FString::Printf(TEXT("%d"), HostingPlayerNum)));
244#endif
245 // We should fill this in with the users name from Apex somehow or somehow otherwise set.
246 Session->OwningUserName = FString(TEXT("PVROUserName"));
247 }
248
249 // Unique identifier of this build for compatibility
250 Session->SessionSettings.BuildUniqueId = GetBuildUniqueId();
251
252 // Setup the host session info
253 FPVROSessionInfo* NewSessionInfo = new FPVROSessionInfo();
254 NewSessionInfo->Init(*Subsystem);
255 Session->SessionInfo = MakeShareable(NewSessionInfo);
256
257 // Create Internet or LAN match.
258 if (NewSessionSettings.bIsLANMatch)
259 {
260 Result = CreateLANSession(HostingPlayerNum, Session);
261 }
262 else
263 {
264 Result = CreateInternetSession(HostingPlayerNum, Session);
265 }
266
267 if (Result != PVRO_IO_PENDING)
268 {
269 // Set the game state as pending (not started)
270 Session->SessionState = EOnlineSessionState::Pending;
271
272 if (Result != PVRO_SUCCESS)
273 {
274 // Clean up the session info so we don't get into a confused state
275 RemoveNamedSession(SessionName);
276 }
277 else
278 {
279 RegisterLocalPlayers(Session);
280 }
281 }
282 }
283 else
284 {
285 Warn("Cannot create session '%s': session already exists.", *SessionName.ToString());
286 }
287
288 if (Result != PVRO_IO_PENDING)
289 {
290 TriggerOnCreateSessionCompleteDelegates(SessionName, (Result == PVRO_SUCCESS) ? true : false);
291 }
292
293 return Result == PVRO_IO_PENDING || Result == PVRO_SUCCESS;
294}
295
296bool FPVROnlineSession::CreateSession(const FUniqueNetId& HostingPlayerId, FName SessionName, const FOnlineSessionSettings& NewSessionSettings)
297{
298 // todo: use proper HostingPlayerId
299 return CreateSession(0, SessionName, NewSessionSettings);
300}
301
303{
304 FScopeLock ScopeLock(&SessionLock);
305
306 bool bResult = false;
307 for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
308 {
309 FNamedOnlineSession& Session = Sessions[SessionIdx];
310 if (NeedsToAdvertise(Session))
311 {
312 bResult = true;
313 break;
314 }
315 }
316
317 return bResult;
318}
319
320bool FPVROnlineSession::NeedsToAdvertise( FNamedOnlineSession& Session )
321{
322 return Session.SessionSettings.bShouldAdvertise && IsHost(Session) &&
323 (
324 (
325 !Session.SessionSettings.bIsLANMatch &&
326 (Session.SessionState != EOnlineSessionState::InProgress || (Session.SessionSettings.bAllowJoinInProgress && Session.NumOpenPublicConnections > 0))
327 )
328 ||
329 (
330 Session.SessionSettings.bAllowJoinViaPresence || Session.SessionSettings.bAllowJoinViaPresenceFriendsOnly
331 )
332 );
333}
334
335bool FPVROnlineSession::IsSessionJoinable(const FNamedOnlineSession& Session) const
336{
337 const FOnlineSessionSettings& Settings = Session.SessionSettings;
338
339 // LAN beacons are implicitly advertised.
340 const bool bIsAdvertised = Settings.bShouldAdvertise || Settings.bIsLANMatch;
341 const bool bIsMatchInProgress = Session.SessionState == EOnlineSessionState::InProgress;
342
343 const bool bJoinableFromProgress = (!bIsMatchInProgress || Settings.bAllowJoinInProgress);
344
345 const bool bAreSpacesAvailable = Session.NumOpenPublicConnections > 0;
346
347 // LAN matches don't care about private connections / invites.
348 // LAN matches don't care about presence information.
349 return bIsAdvertised && bJoinableFromProgress && bAreSpacesAvailable;
350}
351
353{
354 uint32 Result = ONLINE_SUCCESS;
355
356 if ( NeedsToAdvertise() )
357 {
358 // set up LAN session
359 if (LANSessionManager.GetBeaconState() == ELanBeaconState::NotUsingLanBeacon)
360 {
361 FOnValidQueryPacketDelegate QueryPacketDelegate = FOnValidQueryPacketDelegate::CreateRaw(this, &FPVROnlineSession::OnValidQueryPacketReceived);
362 if (!LANSessionManager.Host(QueryPacketDelegate))
363 {
364 Result = ONLINE_FAIL;
365
367 }
368 }
369 }
370 else
371 {
372 if (LANSessionManager.GetBeaconState() != ELanBeaconState::Searching)
373 {
374 // Tear down the LAN beacon
376 }
377 }
378
379 return Result;
380}
381
386
387bool FPVROnlineSession::StartSession(FName SessionName)
388{
389 Log("Starting session %s.", *SessionName.ToString());
390 uint32 Result = PVRO_FAIL;
391 // Grab the session information by name
392 FNamedOnlineSession* Session = GetNamedSession(SessionName);
393 if (Session)
394 {
395 // Can't start a match multiple times
396 if (Session->SessionState == EOnlineSessionState::Pending ||
397 Session->SessionState == EOnlineSessionState::Ended)
398 {
399 if (!Session->SessionSettings.bIsLANMatch)
400 {
401 Result = PVRO_SUCCESS;
402 Session->SessionState = EOnlineSessionState::InProgress;
403 }
404 else
405 {
406 // If this lan match has join in progress disabled, shut down the beacon
407 if (!Session->SessionSettings.bAllowJoinInProgress)
408 {
409 Result = UpdateLANStatus();
410 }
411 Result = PVRO_SUCCESS;
412 Session->SessionState = EOnlineSessionState::InProgress;
413 }
414 }
415 else
416 {
417 Warn("Can't start an online session (%s) in state %s.", *SessionName.ToString(), EOnlineSessionState::ToString(Session->SessionState));
418 }
419 }
420 else
421 {
422 Warn("Can't start an online game for session (%s) that hasn't been created.", *SessionName.ToString());
423 }
424
425 if (Result != PVRO_IO_PENDING)
426 {
427 // Just trigger the delegate
428 TriggerOnStartSessionCompleteDelegates(SessionName, (Result == PVRO_SUCCESS) ? true : false);
429 }
430
431 return Result == PVRO_SUCCESS || Result == PVRO_IO_PENDING;
432}
433
434bool FPVROnlineSession::UpdateSession(FName SessionName, FOnlineSessionSettings& UpdatedSessionSettings, bool bShouldRefreshOnlineData)
435{
436 bool bWasSuccessful = true;
437
438 // Grab the session information by name
439 FNamedOnlineSession* Session = GetNamedSession(SessionName);
440 if (Session)
441 {
442 if (!Session->SessionSettings.bIsLANMatch)
443 {
444 }
445 else
446 {
447 // @TODO ONLINE update LAN settings
448 Session->SessionSettings = UpdatedSessionSettings;
449 TriggerOnUpdateSessionCompleteDelegates(SessionName, bWasSuccessful);
450 }
451 }
452
453 return bWasSuccessful;
454}
455
456bool FPVROnlineSession::EndSession(FName SessionName)
457{
458 Warn("Ending session %s.", *SessionName.ToString());
459
460 uint32 Result = PVRO_FAIL;
461
462 // Grab the session information by name
463 FNamedOnlineSession* Session = GetNamedSession(SessionName);
464 if (Session)
465 {
466 // Can't end a match that isn't in progress
467 if (Session->SessionState == EOnlineSessionState::InProgress)
468 {
469 if (!Session->SessionSettings.bIsLANMatch)
470 {
471 Result = EndInternetSession(Session);
472 }
473 else
474 {
475 Session->SessionState = EOnlineSessionState::Ended;
476
477 // If the session should be advertised and the lan beacon was destroyed, recreate
478 Result = UpdateLANStatus();
479 }
480 }
481 else
482 {
483 Warn("Can't end session (%s) in state %s.", *SessionName.ToString(), EOnlineSessionState::ToString(Session->SessionState));
484 }
485 }
486 else
487 {
488 Warn("Can't end an online game for session (%s) that hasn't been created", *SessionName.ToString());
489 }
490
491 if (Result != PVRO_IO_PENDING)
492 {
493 if (Session)
494 {
495 Session->SessionState = EOnlineSessionState::Ended;
496 }
497
498 TriggerOnEndSessionCompleteDelegates(SessionName, (Result == PVRO_SUCCESS) ? true : false);
499 }
500
501 return Result == PVRO_SUCCESS || Result == PVRO_IO_PENDING;
502}
503
504uint32 FPVROnlineSession::EndInternetSession(FNamedOnlineSession* Session)
505{
506 return PVRO_SUCCESS;
507}
508
509bool FPVROnlineSession::DestroySession(FName SessionName, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
510{
511 uint32 Result = PVRO_FAIL;
512 // Find the session in question
513 FNamedOnlineSession* Session = GetNamedSession(SessionName);
514 if (Session)
515 {
516 if (Session->SessionState != EOnlineSessionState::Destroying)
517 {
518 if (Session->SessionState == EOnlineSessionState::InProgress)
519 {
520 // Enqueue all the end session tasks first
521 EndInternetSession(Session);
522 }
523
524 Result = DestroyInternetSession(Session, CompletionDelegate);
525
526 if (Result != PVRO_IO_PENDING)
527 {
528 // The session info is no longer needed
529 RemoveNamedSession(Session->SessionName);
530 CompletionDelegate.ExecuteIfBound(SessionName, (Result == PVRO_SUCCESS) ? true : false);
531 TriggerOnDestroySessionCompleteDelegates(SessionName, (Result == PVRO_SUCCESS) ? true : false);
532 }
533 }
534 else
535 {
536 Warn("Already in process of destroying session (%s).", *SessionName.ToString());
537 }
538 }
539 else
540 {
541 if (Sessions.Num() > 0)
542 {
543 Warn("Can't destroy an online session that doesn't exist (%s).", *SessionName.ToString());
544 CompletionDelegate.ExecuteIfBound(SessionName, false);
545 TriggerOnDestroySessionCompleteDelegates(SessionName, false);
546 }
547 else
548 {
549 Error("There are no sessions to destroy.\nCheck against the Session Interface's Sessions before calling delete.\nWill not trigger OnDestroySession.");
550 }
551 }
552
553 return Result == PVRO_SUCCESS || Result == PVRO_IO_PENDING;
554}
555
556uint32 FPVROnlineSession::DestroyInternetSession(FNamedOnlineSession* Session, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
557{
558 Session->SessionState = EOnlineSessionState::Destroying;
559 if (Session->SessionInfo.IsValid())
560 {
561 if (GetNumSessions() == 0 || !Subsystem->IsDedicated())
562 {
563 IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
564 if (VoiceInt.IsValid())
565 {
566 if (!Subsystem->IsDedicated())
567 {
568 // Stop local talkers
569 VoiceInt->UnregisterLocalTalkers();
570 }
571
572 // Stop remote voice
573 VoiceInt->RemoveAllRemoteTalkers();
574 }
575 }
576 }
577
578 return PVRO_SUCCESS;
579}
580
581uint32 FPVROnlineSession::DestroyLANSession(FName SessionName, const FOnDestroySessionCompleteDelegate& CompletionDelegate)
582{
583 return UpdateLANStatus();
584}
585
586bool FPVROnlineSession::IsPlayerInSession(FName SessionName, const FUniqueNetId& UniqueId)
587{
588 return IsPlayerInSessionImpl(this, SessionName, UniqueId);
589}
590
591bool FPVROnlineSession::StartMatchmaking(const TArray< TSharedRef<const FUniqueNetId> >& LocalPlayers, FName SessionName, const FOnlineSessionSettings& NewSessionSettings, TSharedRef<FOnlineSessionSearch>& SearchSettings)
592{
593 Warn("StartMatchmaking is not supported on this platform. Use FindSessions or FindSessionById.");
594 TriggerOnMatchmakingCompleteDelegates(SessionName, false);
595 return false;
596}
597
598bool FPVROnlineSession::CancelMatchmaking(int32 SearchingPlayerNum, FName SessionName)
599{
600 Warn("CancelMatchmaking is not supported on this platform. Use CancelFindSessions.");
601 TriggerOnCancelMatchmakingCompleteDelegates(SessionName, false);
602 return false;
603}
604
605bool FPVROnlineSession::CancelMatchmaking(const FUniqueNetId& SearchingPlayerId, FName SessionName)
606{
607 Warn("CancelMatchmaking is not supported on this platform. Use CancelFindSessions.");
608 TriggerOnCancelMatchmakingCompleteDelegates(SessionName, false);
609 return false;
610}
611
612bool FPVROnlineSession::FindSessions(int32 SearchingPlayerNum, const TSharedRef<FOnlineSessionSearch>& SearchSettings)
613{
614 uint32 Return = PVRO_FAIL;
615
616 bool bIsValidCurrentSearch = CurrentSessionSearch.IsValid() ? (CurrentSessionSearch->SearchState == EOnlineAsyncTaskState::InProgress) : false;
617
618 // Don't start another search while one is in progress
619 if (!bIsValidCurrentSearch)
620 {
621 TriggerOnFindSessionsStartDelegates();
622 // Free up previous results
623 SearchSettings->SearchResults.Empty();
624
625 // Copy the search pointer so we can keep it around
626 CurrentSessionSearch = SearchSettings;
627 if (CurrentSessionSearch->TimeoutInSeconds < 120.f)
628 {
629 CurrentSessionSearch->TimeoutInSeconds = 120.f;
630 }
631
632 // Remember the time at which we started search, as this will be used for a "good enough" ping estimation
633 SessionSearchStartTime = FPlatformTime::Seconds();
634
635 // LAN works the same as an Internet session in this case.
636 Return = FindInternetSession(SearchSettings);
637
638 if (Return == PVRO_IO_PENDING)
639 {
640 SearchSettings->SearchState = EOnlineAsyncTaskState::InProgress;
641 }
642
643 if (Return == PVRO_FAIL)
644 {
645 SearchSettings->SearchState = EOnlineAsyncTaskState::Failed;
646 }
647 }
648 else
649 {
650 Log("Ignoring game search request while one is pending");
651 Return = PVRO_IO_PENDING;
652 }
653
654 return Return == PVRO_SUCCESS || Return == PVRO_IO_PENDING;
655}
656
657bool FPVROnlineSession::FindSessions(const FUniqueNetId& SearchingPlayerId, const TSharedRef<FOnlineSessionSearch>& SearchSettings)
658{
659 return FindSessions(0, SearchSettings);
660}
661
662bool FPVROnlineSession::FindSessionById(const FUniqueNetId& SearchingUserId, const FUniqueNetId& SessionId, const FUniqueNetId& FriendId, const FOnSingleSessionResultCompleteDelegate& CompletionDelegates)
663{
664 FOnlineSessionSearchResult EmptyResult;
665 CompletionDelegates.ExecuteIfBound(0, false, EmptyResult);
666 return true;
667}
668
669uint32 FPVROnlineSession::FindInternetSession(const TSharedRef<FOnlineSessionSearch>& SearchSettings)
670{
671 uint32 Return = PVRO_IO_PENDING;
672
673 int32 OrgId = -1, ModuleId = -1;
674 FString ModuleVersion, UserName;
675 SearchSettings->QuerySettings.Get<int32>(FName(TEXT("OrgID")), OrgId);
676 SearchSettings->QuerySettings.Get<int32>(FName(TEXT("ModuleID")), ModuleId);
677 SearchSettings->QuerySettings.Get<FString>(FName(TEXT("ModuleVersion")), ModuleVersion);
678 SearchSettings->QuerySettings.Get<FString>(FName(TEXT("UserName")), UserName);
679 // TODO: Add it to accept search settings
680 if (!Subsystem->FindSessions(OrgId, ModuleId, UserName, ModuleVersion))
681 {
682 Return = PVRO_FAIL;
683 }
684
685 return PVRO_IO_PENDING;
686}
687
688uint32 FPVROnlineSession::FindLANSession(const TSharedRef<FOnlineSessionSearch>& SearchSettings)
689{
690 uint32 Return = PVRO_SUCCESS;//PVRO_IO_PENDING;
691
692 // Honestly, we'll probably just replace this with the internet session find and point to the local IP. No reason for sillyness.
693
694 return Return;
695}
696
697bool FPVROnlineSession::ClearSessionSearch(EOnlineAsyncTaskState::Type TaskState)
698{
699 Log("Clearing session search data.");
700 bool bCouldClear = false;
701
702 if (CurrentSessionSearch.IsValid())
703 {
704 // Make sure it's the right type
705 bCouldClear = true;
706 CurrentSessionSearch->SearchState = TaskState;
707 CurrentSessionSearch = nullptr;
708 }
709 else
710 {
711 Warn("No session search to clear.");
712 }
713
714 return bCouldClear;
715}
716
718{
719 Log("Canceling session search.");
720 uint32 Return = PVRO_FAIL;
721 if (CurrentSessionSearch.IsValid() && CurrentSessionSearch->SearchState == EOnlineAsyncTaskState::InProgress)
722 {
724
725 Return = ClearSessionSearch(EOnlineAsyncTaskState::Failed);
726 }
727 else
728 {
729 Warn("Can't cancel a search that isn't in progress");
730 }
731
732 if (Return != PVRO_IO_PENDING)
733 {
734 TriggerOnCancelFindSessionsCompleteDelegates(true);
735 }
736
737 return Return == PVRO_SUCCESS || Return == PVRO_IO_PENDING;
738}
739
740void FPVROnlineSession::GetHostAndPort(int32& Host, int32& Port, const FString& HostIP, const FString& HostPort)
741{
742 // Convert the HostAddress and Port into their appropriate forms.
743 FIPv4Address HostAddress;
744 bool Result = FIPv4Address::Parse(HostIP, HostAddress);
745 if (!Result)
746 {
747 ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
748 if (SocketSubsystem)
749 {
750 FResolveInfo* ResolveInfo = SocketSubsystem->GetHostByName(TCHAR_TO_ANSI(*HostIP));
751 while (!ResolveInfo->IsComplete());
752 if (ResolveInfo->GetErrorCode() == 0)
753 {
754 const FInternetAddr* Address = &ResolveInfo->GetResolvedAddress();
755 uint32 IP = 0;
756 Address->GetIp(IP);
757 Host = FIPv4Address(IP).Value;
758 }
759 }
760 else
761 {
762 // TODO SocketSubsystem issue?
763 Warn("FindSession: No SocketSubsystem found.");
764 }
765 }
766 else
767 {
768 Host = HostAddress.Value;
769 }
770
771 // +1 is a workaround because this implementation uses 2 ports for communication. The user shouldn't bother
772 // to add this inside the BP or config files.
773 Port = FCString::Atoi(*HostPort);
774}
775
776bool FPVROnlineSession::JoinSession(int32 PlayerNum, FName SessionName, const FOnlineSessionSearchResult& DesiredSession)
777{
778 Log("Attemping to join session %s.", *DesiredSession.GetSessionIdStr());
779 uint32 Return = PVRO_FAIL;
780 FNamedOnlineSession* Session = GetNamedSession(SessionName);
781
782 // Don't join a session if already in one or hosting one
783 if (Session == nullptr)
784 {
785 TriggerOnJoinSessionStartDelegates();
786
787 // Remove any local talkers
788 IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
789 if (VoiceInt.IsValid())
790 {
791 if (!Subsystem->IsDedicated())
792 {
793 // Stop local talkers
794 VoiceInt->UnregisterLocalTalkers();
795 }
796
797 // Stop remote voice
798 VoiceInt->RemoveAllRemoteTalkers();
799 }
800
801 // Create a named session from the search result data
802 Session = AddNamedSession(SessionName, DesiredSession.Session);
803 Session->HostingPlayerNum = PlayerNum;
804
805 // Join a session
806 FPVROSessionInfo* NewSessionInfo = new FPVROSessionInfo();
807 Session->SessionInfo = MakeShareable(NewSessionInfo);
808
809 Return = JoinInternetSession(PlayerNum, Session, &DesiredSession.Session);
810
811 // turn off advertising on Join, to avoid clients advertising it over LAN
812 Session->SessionSettings.bShouldAdvertise = false;
813 if (Return != PVRO_IO_PENDING)
814 {
815 if (Return != PVRO_SUCCESS)
816 {
817 // Clean up the session info so we don't get into a confused state
818 RemoveNamedSession(SessionName);
819 }
820 else
821 {
822 RegisterLocalPlayers(Session);
823 }
824 }
825 }
826 else
827 {
828 Warn("Session (%s) already exists, can't join twice.", *SessionName.ToString());
829 }
830
831 if (Return != PVRO_IO_PENDING)
832 {
833 // Just trigger the delegate as having failed
834 TriggerOnJoinSessionCompleteDelegates(SessionName, Return == PVRO_SUCCESS ? EOnJoinSessionCompleteResult::Success : EOnJoinSessionCompleteResult::UnknownError);
835 }
836
837 return Return == PVRO_SUCCESS || Return == PVRO_IO_PENDING;
838}
839
840bool FPVROnlineSession::JoinSession(const FUniqueNetId& PlayerId, FName SessionName, const FOnlineSessionSearchResult& DesiredSession)
841{
842 // Assuming player 0 should be OK here
843 return JoinSession(0, SessionName, DesiredSession);
844}
845
846bool FPVROnlineSession::FindFriendSession(int32 LocalUserNum, const FUniqueNetId& Friend)
847{
848 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
849 TArray<FOnlineSessionSearchResult> EmptySearchResult;
850 TriggerOnFindFriendSessionCompleteDelegates(LocalUserNum, false, EmptySearchResult);
851 return false;
852};
853
854bool FPVROnlineSession::FindFriendSession(const FUniqueNetId& LocalUserId, const FUniqueNetId& Friend)
855{
856 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
857 TArray<FOnlineSessionSearchResult> EmptySearchResult;
858 TriggerOnFindFriendSessionCompleteDelegates(0, false, EmptySearchResult);
859 return false;
860}
861
862bool FPVROnlineSession::FindFriendSession(const FUniqueNetId& LocalUserId, const TArray<TSharedRef<const FUniqueNetId>>& FriendList)
863{
864 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
865 TArray<FOnlineSessionSearchResult> EmptySearchResult;
866 TriggerOnFindFriendSessionCompleteDelegates(0, false, EmptySearchResult);
867 return false;
868}
869
870bool FPVROnlineSession::SendSessionInviteToFriend(int32 LocalUserNum, FName SessionName, const FUniqueNetId& Friend)
871{
872 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
873 return false;
874};
875
876bool FPVROnlineSession::SendSessionInviteToFriend(const FUniqueNetId& LocalUserId, FName SessionName, const FUniqueNetId& Friend)
877{
878 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
879 return false;
880}
881
882bool FPVROnlineSession::SendSessionInviteToFriends(int32 LocalUserNum, FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Friends)
883{
884 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
885 return false;
886};
887
888bool FPVROnlineSession::SendSessionInviteToFriends(const FUniqueNetId& LocalUserId, FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Friends)
889{
890 // this function has to exist due to interface definition, but it does not have a meaningful implementation in PixoVR subsystem
891 return false;
892}
893
894uint32 FPVROnlineSession::JoinInternetSession(int32 PlayerNum, FNamedOnlineSession* Session, const FOnlineSession* SearchSession)
895{
896 uint32 Result = PVRO_FAIL;
897 Session->SessionState = EOnlineSessionState::Pending;
898
899 if (Session->SessionInfo.IsValid() && SearchSession != nullptr && SearchSession->SessionInfo.IsValid())
900 {
901 // Copy the session info over
902 const FPVROSessionInfo* SearchSessionInfo = (const FPVROSessionInfo*)SearchSession->SessionInfo.Get();
903 FPVROSessionInfo* SessionInfo = (FPVROSessionInfo*)Session->SessionInfo.Get();
904 SessionInfo->SessionId = SearchSessionInfo->SessionId;
905
906 uint32 IpAddr;
907 SearchSessionInfo->HostAddr->GetIp(IpAddr);
908 SessionInfo->HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
909 SessionInfo->HostAddr->SetIp(IpAddr);
910 SessionInfo->HostAddr->SetPort(SearchSessionInfo->HostAddr->GetPort());
911
912 Result = PVRO_SUCCESS;
913 }
914
915 return Result;
916}
917
918bool FPVROnlineSession::PingSearchResults(const FOnlineSessionSearchResult& SearchResult)
919{
920 return false;
921}
922
924static bool GetConnectStringFromSessionInfo(TSharedPtr<FPVROSessionInfo>& SessionInfo, FString& ConnectInfo, int32 PortOverride=0)
925{
926 bool bSuccess = false;
927 if (SessionInfo.IsValid())
928 {
929 if (SessionInfo->HostAddr.IsValid() && SessionInfo->HostAddr->IsValid())
930 {
931 if (PortOverride != 0)
932 {
933 ConnectInfo = FString::Printf(TEXT("%s:%d"), *SessionInfo->HostAddr->ToString(false), PortOverride);
934 }
935 else
936 {
937 ConnectInfo = FString::Printf(TEXT("%s"), *SessionInfo->HostAddr->ToString(true));
938 }
939
940 bSuccess = true;
941 }
942 }
943
944 return bSuccess;
945}
946
947bool FPVROnlineSession::GetResolvedConnectString(FName SessionName, FString& ConnectInfo, FName PortType)
948{
949 bool bSuccess = false;
950 // Find the session
951 FNamedOnlineSession* Session = GetNamedSession(SessionName);
952 if (Session != nullptr)
953 {
954 TSharedPtr<FPVROSessionInfo> SessionInfo = StaticCastSharedPtr<FPVROSessionInfo>(Session->SessionInfo);
955 if (PortType == NAME_BeaconPort)
956 {
957 int32 BeaconListenPort = GetBeaconPortFromSessionSettings(Session->SessionSettings);
958 bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo, BeaconListenPort);
959 }
960 else if (PortType == NAME_GamePort)
961 {
962 bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo);
963 }
964
965 if (!bSuccess)
966 {
967 Warn("Invalid session info for session %s in GetResolvedConnectString()", *SessionName.ToString());
968 }
969 }
970 else
971 {
972 Warn("Unknown session name (%s) specified to GetResolvedConnectString().", *SessionName.ToString());
973 }
974
975 return bSuccess;
976}
977
978bool FPVROnlineSession::GetResolvedConnectString(const FOnlineSessionSearchResult& SearchResult, FName PortType, FString& ConnectInfo)
979{
980 bool bSuccess = false;
981 if (SearchResult.Session.SessionInfo.IsValid())
982 {
983 TSharedPtr<FPVROSessionInfo> SessionInfo = StaticCastSharedPtr<FPVROSessionInfo>(SearchResult.Session.SessionInfo);
984
985 if (PortType == NAME_BeaconPort)
986 {
987 int32 BeaconListenPort = GetBeaconPortFromSessionSettings(SearchResult.Session.SessionSettings);
988 bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo, BeaconListenPort);
989
990 }
991 else if (PortType == NAME_GamePort)
992 {
993 bSuccess = GetConnectStringFromSessionInfo(SessionInfo, ConnectInfo);
994 }
995 }
996
997 if (!bSuccess || ConnectInfo.IsEmpty())
998 {
999 Warn("Invalid session info in search result to GetResolvedConnectString().");
1000 }
1001
1002 return bSuccess;
1003}
1004
1005FOnlineSessionSettings* FPVROnlineSession::GetSessionSettings(FName SessionName)
1006{
1007 FNamedOnlineSession* Session = GetNamedSession(SessionName);
1008 if (Session)
1009 {
1010 return &Session->SessionSettings;
1011 }
1012 return nullptr;
1013}
1014
1015void FPVROnlineSession::RegisterLocalPlayers(FNamedOnlineSession* Session)
1016{
1017 if (!Subsystem->IsDedicated())
1018 {
1019 IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
1020 if (VoiceInt.IsValid())
1021 {
1022 for (int32 Index = 0; Index < MAX_LOCAL_PLAYERS; Index++)
1023 {
1024 // Register the local player as a local talker
1025 VoiceInt->RegisterLocalTalker(Index);
1026 }
1027 }
1028 }
1029}
1030
1031void FPVROnlineSession::RegisterVoice(const FUniqueNetId& PlayerId)
1032{
1033 if (!Subsystem->IsDedicated())
1034 {
1035 IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
1036 if (VoiceInt.IsValid())
1037 {
1038 if (!Subsystem->IsLocalPlayer(PlayerId))
1039 {
1040 VoiceInt->RegisterRemoteTalker(PlayerId);
1041 }
1042 else
1043 {
1044 // This is a local player. In case their PlayerState came last during replication, reprocess muting
1045 VoiceInt->ProcessMuteChangeNotification();
1046 }
1047 }
1048 }
1049}
1050
1051void FPVROnlineSession::UnregisterVoice(const FUniqueNetId& PlayerId)
1052{
1053 if (!Subsystem->IsDedicated())
1054 {
1055 IOnlineVoicePtr VoiceInt = Subsystem->GetVoiceInterface();
1056 if (VoiceInt.IsValid())
1057 {
1058 if (!Subsystem->IsLocalPlayer(PlayerId))
1059 {
1060 if (VoiceInt.IsValid())
1061 {
1062 VoiceInt->UnregisterRemoteTalker(PlayerId);
1063 }
1064 }
1065 }
1066 }
1067}
1068
1069bool FPVROnlineSession::RegisterPlayer(FName SessionName, const FUniqueNetId& PlayerId, bool bWasInvited)
1070{
1071#if ENGINE_MAJOR_VERSION >= 5
1072 TArray<FUniqueNetIdRef> Players;
1073 Players.Add(FUniqueNetIdPixoVR::Create(PlayerId));
1074#else
1075 TArray<TSharedRef<const FUniqueNetId>> Players;
1076 Players.Add(MakeShareable(new FUniqueNetIdPixoVR(PlayerId)));
1077#endif
1078 return RegisterPlayers(SessionName, Players, bWasInvited);
1079}
1080
1081bool FPVROnlineSession::RegisterPlayers(FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Players, bool bWasInvited)
1082{
1083 bool bSuccess = false;
1084 FNamedOnlineSession* Session = GetNamedSession(SessionName);
1085 if (Session)
1086 {
1087 bSuccess = true;
1088
1089 for (int32 PlayerIdx=0; PlayerIdx<Players.Num(); PlayerIdx++)
1090 {
1091 const TSharedRef<const FUniqueNetId>& PlayerId = Players[PlayerIdx];
1092
1093 FUniqueNetIdMatcher PlayerMatch(*PlayerId);
1094 if (Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch) == INDEX_NONE)
1095 {
1096 Session->RegisteredPlayers.Add(PlayerId);
1097 RegisterVoice(*PlayerId);
1098 }
1099 else
1100 {
1101 RegisterVoice(*PlayerId);
1102 Log("Player %s already registered in session %s.", *PlayerId->ToDebugString(), *SessionName.ToString());
1103 }
1104 }
1105 }
1106 else
1107 {
1108 Warn("No game present to join for session (%s).", *SessionName.ToString());
1109 }
1110
1111 TriggerOnRegisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
1112 return bSuccess;
1113}
1114
1115bool FPVROnlineSession::UnregisterPlayer(FName SessionName, const FUniqueNetId& PlayerId)
1116{
1117#if ENGINE_MAJOR_VERSION >= 5
1118 TArray<FUniqueNetIdRef> Players;
1119 Players.Add(FUniqueNetIdPixoVR::Create(PlayerId));
1120#else
1121 TArray<TSharedRef<const FUniqueNetId>> Players;
1122 Players.Add(MakeShareable(new FUniqueNetIdPixoVR(PlayerId)));
1123#endif
1124 return UnregisterPlayers(SessionName, Players);
1125}
1126
1127bool FPVROnlineSession::UnregisterPlayers(FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Players)
1128{
1129 bool bSuccess = true;
1130
1131 FNamedOnlineSession* Session = GetNamedSession(SessionName);
1132 if (Session)
1133 {
1134 for (int32 PlayerIdx=0; PlayerIdx < Players.Num(); PlayerIdx++)
1135 {
1136 const TSharedRef<const FUniqueNetId>& PlayerId = Players[PlayerIdx];
1137
1138 FUniqueNetIdMatcher PlayerMatch(*PlayerId);
1139 int32 RegistrantIndex = Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch);
1140 if (RegistrantIndex != INDEX_NONE)
1141 {
1142 Session->RegisteredPlayers.RemoveAtSwap(RegistrantIndex);
1143 UnregisterVoice(*PlayerId);
1144 }
1145 else
1146 {
1147 Warn("Player %s is not part of session (%s).", *PlayerId->ToDebugString(), *SessionName.ToString());
1148 }
1149 }
1150 }
1151 else
1152 {
1153 Warn("No game present to leave for session (%s).", *SessionName.ToString());
1154 bSuccess = false;
1155 }
1156
1157 TriggerOnUnregisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
1158 return bSuccess;
1159}
1160
1161void FPVROnlineSession::Tick(float DeltaTime)
1162{
1163 SCOPE_CYCLE_COUNTER(STAT_Session_Interface);
1164 TickLanTasks(DeltaTime);
1165
1166 if (CurrentSessionSearch.IsValid())
1167 {
1168 float CurrentTimeInSeconds = FPlatformTime::Seconds();
1169 if (CurrentTimeInSeconds - SessionSearchStartTime > CurrentSessionSearch->TimeoutInSeconds)
1170 {
1172 }
1173 }
1174}
1175
1177{
1178 LANSessionManager.Tick(DeltaTime);
1179}
1180
1181// TODO: Evaluate if this is still needed
1182void FPVROnlineSession::OnValidQueryPacketReceived(uint8* PacketData, int32 PacketLength, uint64 ClientNonce)
1183{
1184 Log("Executing OnValidQueryPacketReceived.");
1185
1186 // Iterate through all registered sessions and respond for each one that can be joinable
1187 FScopeLock ScopeLock(&SessionLock);
1188 for (int32 SessionIndex = 0; SessionIndex < Sessions.Num(); SessionIndex++)
1189 {
1190 Warn("OnValidQueryPacketReceived Session is Registered.");
1191
1192 FNamedOnlineSession* Session = &Sessions[SessionIndex];
1193
1194 // Don't respond to query if the session is not a joinable LAN match.
1195 if (Session)
1196 {
1197 Warn("Session valid trough Session Index.");
1198
1199 const FOnlineSessionSettings& Settings = Session->SessionSettings;
1200
1201 const bool bIsMatchInProgress = Session->SessionState == EOnlineSessionState::InProgress;
1202
1203 const bool bIsMatchJoinable = (!bIsMatchInProgress || Settings.bAllowJoinInProgress) && Settings.NumPublicConnections > 0;
1204 if (bIsMatchJoinable)
1205 {
1206 Warn("Match from query is joinable.");
1207 }
1208 }
1209 }
1210}
1211
1212// TODO: Evaluate if this is still needed
1213void FPVROnlineSession::OnValidResponsePacketReceived(uint8* PacketData, int32 PacketLength)
1214{
1215 // Create an object that we'll copy the data to
1216 FOnlineSessionSettings NewServer;
1217 if (CurrentSessionSearch.IsValid())
1218 {
1219 // Add space in the search results array
1220 FOnlineSessionSearchResult* NewResult = new (CurrentSessionSearch->SearchResults) FOnlineSessionSearchResult();
1221 // this is not a correct ping, but better than nothing
1222 NewResult->PingInMs = static_cast<int32>((FPlatformTime::Seconds() - SessionSearchStartTime) * 1000);
1223
1224 FOnlineSession* NewSession = &NewResult->Session;
1225 }
1226 else
1227 {
1228 Warn("Failed to create new online game settings object.");
1229 }
1230}
1231
1233{
1234 if (LANSessionManager.GetBeaconState() == ELanBeaconState::Searching)
1235 {
1237 }
1238
1239 return UpdateLANStatus();
1240}
1241
1243{
1245
1246 if (CurrentSessionSearch.IsValid())
1247 {
1248 if (CurrentSessionSearch->SearchResults.Num() > 0)
1249 {
1250 // Allow game code to sort the servers
1251 CurrentSessionSearch->SortSearchResults();
1252 }
1253 CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::Done;
1254
1255 CurrentSessionSearch = nullptr;
1256 }
1257
1258 // Trigger the delegate as complete
1259 TriggerOnFindSessionsCompleteDelegates(true);
1260}
1261
1263{
1264 FScopeLock ScopeLock(&SessionLock);
1265 return Sessions.Num();
1266}
1267
1269{
1270 FScopeLock ScopeLock(&SessionLock);
1271
1272 for (int32 SessionIdx=0; SessionIdx < Sessions.Num(); SessionIdx++)
1273 {
1274 DumpNamedSession(&Sessions[SessionIdx]);
1275 }
1276}
1277
1278void FPVROnlineSession::RegisterLocalPlayer(const FUniqueNetId& PlayerId, FName SessionName, const FOnRegisterLocalPlayerCompleteDelegate& Delegate)
1279{
1280 Delegate.ExecuteIfBound(PlayerId, EOnJoinSessionCompleteResult::Success);
1281}
1282
1283void FPVROnlineSession::UnregisterLocalPlayer(const FUniqueNetId& PlayerId, FName SessionName, const FOnUnregisterLocalPlayerCompleteDelegate& Delegate)
1284{
1285 Delegate.ExecuteIfBound(PlayerId, true);
1286}
1287
1288void FPVROnlineSession::SetPortFromNetDriver(const FPVROSubsystem& Subsystem, const TSharedPtr<FOnlineSessionInfo>& SessionInfo)
1289{
1290 auto NetDriverPort = GetPortFromNetDriver(Subsystem.GetInstanceName());
1291 auto SessionInfoPixoVR = StaticCastSharedPtr<FPVROSessionInfo>(SessionInfo);
1292 if (SessionInfoPixoVR.IsValid() && SessionInfoPixoVR->HostAddr.IsValid())
1293 {
1294 SessionInfoPixoVR->HostAddr->SetPort(NetDriverPort);
1295 }
1296}
1297
1298bool FPVROnlineSession::IsHost(const FNamedOnlineSession& Session) const
1299{
1300 if (Subsystem->IsDedicated())
1301 {
1302 return true;
1303 }
1304
1305#if ENGINE_MAJOR_VERSION >= 5
1306 FUniqueNetIdPixoVRRef UserId = FUniqueNetIdPixoVR::Create(FString::Printf(TEXT("%d"), Session.HostingPlayerNum));
1307 return (UserId->IsValid() && (*UserId == *Session.OwningUserId));
1308#else
1309 TSharedPtr<const FUniqueNetId> UserId = MakeShareable(new FUniqueNetIdPixoVR(FString::Printf(TEXT("%d"), Session.HostingPlayerNum)));
1310 return (UserId.IsValid() && (*UserId == *Session.OwningUserId));
1311#endif
1312}
1313
1314TSharedPtr<const FUniqueNetId> FPVROnlineSession::CreateSessionIdFromString(const FString& SessionIdStr)
1315{
1316#if ENGINE_MAJOR_VERSION >= 5
1317 FUniqueNetIdPtr SessionId;
1318 if (!SessionIdStr.IsEmpty())
1319 {
1320 SessionId = FUniqueNetIdPixoVR::Create(SessionIdStr);
1321 }
1322#else
1323 FUniqueNetIdPtr SessionId;
1324 if (!SessionIdStr.IsEmpty())
1325 {
1326 SessionId = MakeShared<FUniqueNetIdPixoVR>(SessionIdStr);
1327 }
1328#endif
1329
1330 return SessionId;
1331}
1332
1334{
1335 FString ServerName = TEXT("");
1336 if(FParse::Value(FCommandLine::Get(), TEXT("-PIXOVR_SERVER_NAME="), ServerName))
1337 {
1338 }
1339
1340 return ServerName;
1341}
1342
1343#undef Fatal
1344#undef Error
1345#undef Warn
1346#undef Log
#define Error(pmt,...)
#define Warn(pmt,...)
#define Log(pmt,...)
FOnPortChanged::FDelegate FOnPortChangedDelegate
Definition PVROBeacon.h:33
FOnValidQueryPacket::FDelegate FOnValidQueryPacketDelegate
Definition PVROBeacon.h:24
TSharedPtr< const FUniqueNetId > FUniqueNetIdPtr
#define PVRO_IO_PENDING
#define PVRO_SUCCESS
#define PVRO_FAIL
static bool GetConnectStringFromSessionInfo(TSharedPtr< FPVROSessionInfo > &SessionInfo, FString &ConnectInfo, int32 PortOverride=0)
#define MAX_PUBLIC_CONNECTIONS
#define Log(pmt,...)
DEFINE_LOG_CATEGORY_STATIC(LogPVROnlineSession, Log, All)
void Tick(float DeltaTime)
ELanBeaconState::Type GetBeaconState() const
Definition PVROBeacon.h:255
bool Host(FOnValidQueryPacketDelegate &QueryDelegate, int32 Port=-1)
void StopLANSession()
TSharedPtr< class FInternetAddr > HostAddr
FUniqueNetIdPixoVR SessionId
void Init(const FPVROSubsystem &Subsystem)
bool FindSessions(int InOrgId=-1, int InModuleId=-1, FString InUserName="", FString InModuleVersion="")
MultiplayerWebSocket WebSocket
virtual IOnlineIdentityPtr GetIdentityInterface() const override
virtual IOnlineVoicePtr GetVoiceInterface() const override
virtual bool PingSearchResults(const FOnlineSessionSearchResult &SearchResult) override
FString GetCustomDedicatedServerName() const
Returns the custom server name if specified as argument.
uint32 CreateInternetSession(int32 HostingPlayerNum, FNamedOnlineSession *Session)
uint32 CreateLANSession(int32 HostingPlayerNum, FNamedOnlineSession *Session)
virtual bool UnregisterPlayers(FName SessionName, const TArray< TSharedRef< const FUniqueNetId > > &Players) override
virtual bool EndSession(FName SessionName) override
virtual bool JoinSession(int32 PlayerNum, FName SessionName, const FOnlineSessionSearchResult &DesiredSession) override
virtual bool RegisterPlayers(FName SessionName, const TArray< TSharedRef< const FUniqueNetId > > &Players, bool bWasInvited=false) override
void GetHostAndPort(int32 &Host, int32 &Port, const FString &HostIP, const FString &HostPort)
void CheckMatchesFound(TSharedPtr< FJsonObject > &JsonMessageObject)
void OnSessionListenPortChanged(int32 Port)
void Tick(float DeltaTime)
void OnValidQueryPacketReceived(uint8 *PacketData, int32 PacketLength, uint64 ClientNonce)
uint32 DestroyLANSession(FName SessionName, const FOnDestroySessionCompleteDelegate &CompletionDelegate=FOnDestroySessionCompleteDelegate())
FNamedOnlineSession * GetNamedSession(FName SessionName) override
void HandleMessageReceived(const FString &InJsonMessage)
virtual bool FindSessions(int32 SearchingPlayerNum, const TSharedRef< FOnlineSessionSearch > &SearchSettings) override
virtual bool CancelFindSessions() override
TSharedPtr< FOnlineSessionSearch > CurrentSessionSearch
virtual TSharedPtr< const FUniqueNetId > CreateSessionIdFromString(const FString &SessionIdStr) override
bool ClearSessionSearch(EOnlineAsyncTaskState::Type TaskState=EOnlineAsyncTaskState::Done)
virtual bool IsPlayerInSession(FName SessionName, const FUniqueNetId &UniqueId) override
uint32 FindInternetSession(const TSharedRef< FOnlineSessionSearch > &SearchSettings)
static void SetPortFromNetDriver(const FPVROSubsystem &Subsystem, const TSharedPtr< FOnlineSessionInfo > &SessionInfo)
uint32 EndInternetSession(FNamedOnlineSession *Session)
uint32 JoinInternetSession(int32 PlayerNum, class FNamedOnlineSession *Session, const class FOnlineSession *SearchSession)
virtual bool FindFriendSession(int32 LocalUserNum, const FUniqueNetId &Friend) override
void OnValidResponsePacketReceived(uint8 *PacketData, int32 PacketLength)
virtual bool StartSession(FName SessionName) override
class FNamedOnlineSession * AddNamedSession(FName SessionName, const FOnlineSessionSettings &SessionSettings) override
virtual bool DestroySession(FName SessionName, const FOnDestroySessionCompleteDelegate &CompletionDelegate=FOnDestroySessionCompleteDelegate()) override
virtual bool RegisterPlayer(FName SessionName, const FUniqueNetId &PlayerId, bool bWasInvited) override
virtual void RegisterLocalPlayer(const FUniqueNetId &PlayerId, FName SessionName, const FOnRegisterLocalPlayerCompleteDelegate &Delegate) override
virtual void RemoveNamedSession(FName SessionName) override
virtual FOnlineSessionSettings * GetSessionSettings(FName SessionName) override
void RegisterVoice(const FUniqueNetId &PlayerId)
FCriticalSection SessionLock
virtual bool GetResolvedConnectString(FName SessionName, FString &ConnectInfo, FName PortType) override
void UnregisterVoice(const FUniqueNetId &PlayerId)
virtual bool CreateSession(int32 HostingPlayerNum, FName SessionName, const FOnlineSessionSettings &NewSessionSettings) override
virtual bool FindSessionById(const FUniqueNetId &SearchingUserId, const FUniqueNetId &SessionId, const FUniqueNetId &FriendId, const FOnSingleSessionResultCompleteDelegate &CompletionDelegate) override
uint32 DestroyInternetSession(FNamedOnlineSession *Session, const FOnDestroySessionCompleteDelegate &CompletionDelegate=FOnDestroySessionCompleteDelegate())
bool IsHost(const FNamedOnlineSession &Session) const
virtual int32 GetNumSessions() override
virtual void UnregisterLocalPlayer(const FUniqueNetId &PlayerId, FName SessionName, const FOnUnregisterLocalPlayerCompleteDelegate &Delegate) override
virtual bool SendSessionInviteToFriend(int32 LocalUserNum, FName SessionName, const FUniqueNetId &Friend) override
virtual bool StartMatchmaking(const TArray< TSharedRef< const FUniqueNetId > > &LocalPlayers, FName SessionName, const FOnlineSessionSettings &NewSessionSettings, TSharedRef< FOnlineSessionSearch > &SearchSettings) override
TArray< FNamedOnlineSession > Sessions
virtual bool UnregisterPlayer(FName SessionName, const FUniqueNetId &PlayerId) override
virtual bool CancelMatchmaking(int32 SearchingPlayerNum, FName SessionName) override
FPVROSession LANSessionManager
void TickLanTasks(float DeltaTime)
virtual bool SendSessionInviteToFriends(int32 LocalUserNum, FName SessionName, const TArray< TSharedRef< const FUniqueNetId > > &Friends) override
uint32 FindLANSession(const TSharedRef< FOnlineSessionSearch > &SearchSettings)
bool IsSessionJoinable(const FNamedOnlineSession &Session) const
virtual void DumpSessionState() override
virtual bool UpdateSession(FName SessionName, FOnlineSessionSettings &UpdatedSessionSettings, bool bShouldRefreshOnlineData=true) override
void RegisterLocalPlayers(class FNamedOnlineSession *Session)
class FPVROSubsystem * Subsystem
FSocketMessageReceived & OnWebSocketMessageReceived()
FSocketDisconnected & OnWebSocketDisconnected()
FSocketConnectFailed & OnWebSocketConnectFailed()
FString OwningUserName
UPROPERTY()
FString MapName
UPROPERTY()
FString SessionID
UPROPERTY()
FString Port
UPROPERTY()
FString IPAddress
UPROPERTY()