Documentation for the Unreal C++ Library
Loading...
Searching...
No Matches
PVROSubsystem.cpp
Go to the documentation of this file.
1// Copyright(c) 2023 PixoVR, LLC. All Rights Reserved.
2
3#include "PVROSubsystem.h"
4#include "HAL/RunnableThread.h"
6#include "PVROnlineSession.h"
8#include "VoiceInterfaceImpl.h"
10#include "Misc/CommandLine.h"
11
12DEFINE_LOG_CATEGORY_STATIC(LogPVROSubSystem, Log, All);
13
14#define Verbose(pmt, ...) UE_LOG(LogPVROSubSystem, Verbose, TEXT(pmt), ##__VA_ARGS__)
15#define Log(pmt, ...) UE_LOG(LogPVROSubSystem, Log, TEXT(pmt), ##__VA_ARGS__)
16#define Warn(pmt, ...) UE_LOG(LogPVROSubSystem, Warning, TEXT(pmt), ##__VA_ARGS__)
17#define Error(pmt, ...) UE_LOG(LogPVROSubSystem, Error, TEXT(pmt), ##__VA_ARGS__)
18#define Fatal(pmt, ...) UE_LOG(LogPVROSubSystem, Fatal, TEXT(pmt), ##__VA_ARGS__)
19
20FThreadSafeCounter FPVROSubsystem::TaskCounter;
21
22IOnlineSessionPtr FPVROSubsystem::GetSessionInterface() const
23{
24 return SessionInterface;
25}
26
27IOnlineFriendsPtr FPVROSubsystem::GetFriendsInterface() const
28{
29 return nullptr;
30}
31
32IOnlinePartyPtr FPVROSubsystem::GetPartyInterface() const
33{
34 return nullptr;
35}
36
37IOnlineGroupsPtr FPVROSubsystem::GetGroupsInterface() const
38{
39 return nullptr;
40}
41
42IOnlineSharedCloudPtr FPVROSubsystem::GetSharedCloudInterface() const
43{
44 return nullptr;
45}
46
47IOnlineUserCloudPtr FPVROSubsystem::GetUserCloudInterface() const
48{
49 return nullptr;
50}
51
52IOnlineEntitlementsPtr FPVROSubsystem::GetEntitlementsInterface() const
53{
54 return nullptr;
55};
56
57IOnlineLeaderboardsPtr FPVROSubsystem::GetLeaderboardsInterface() const
58{
59 return nullptr;
60}
61
62IOnlineVoicePtr FPVROSubsystem::GetVoiceInterface() const
63{
65 {
66 if (!VoiceInterface->Init())
67 {
68 VoiceInterface = nullptr;
69 }
70
72 }
73
74 return VoiceInterface;
75}
76
77IOnlineExternalUIPtr FPVROSubsystem::GetExternalUIInterface() const
78{
79 return nullptr;
80}
81
83{
84 return nullptr;
85}
86
87IOnlineIdentityPtr FPVROSubsystem::GetIdentityInterface() const
88{
89 return IdentityInterface;
90}
91
92IOnlineTitleFilePtr FPVROSubsystem::GetTitleFileInterface() const
93{
94 return nullptr;
95}
96
97IOnlineEventsPtr FPVROSubsystem::GetEventsInterface() const
98{
99 return nullptr;
100}
101
102IOnlineAchievementsPtr FPVROSubsystem::GetAchievementsInterface() const
103{
104 return nullptr;
105}
106
107IOnlineSharingPtr FPVROSubsystem::GetSharingInterface() const
108{
109 return nullptr;
110}
111
113{
114 return nullptr;
115}
116
117IOnlineMessagePtr FPVROSubsystem::GetMessageInterface() const
118{
119 return nullptr;
120}
121
122IOnlinePresencePtr FPVROSubsystem::GetPresenceInterface() const
123{
124 return nullptr;
125}
126
128{
129 return nullptr;
130}
131
132IOnlineTurnBasedPtr FPVROSubsystem::GetTurnBasedInterface() const
133{
134 return nullptr;
135}
136
137IOnlineTournamentPtr FPVROSubsystem::GetTournamentInterface() const
138{
139 return nullptr;
140}
141
143{
144 return nullptr;
145}
146
147void FPVROSubsystem::QueueAsyncTask(FOnlineAsyncTask* AsyncTask)
148{
149 check(TaskThreadManager);
150 TaskThreadManager->AddToInQueue(AsyncTask);
151}
152
153bool FPVROSubsystem::Tick(float DeltaTime)
154{
155 if (!FOnlineSubsystemImpl::Tick(DeltaTime))
156 {
157 return false;
158 }
159
161 {
162 TaskThreadManager->GameTick();
163 }
164
165 if (SessionInterface.IsValid())
166 {
167 SessionInterface->Tick(DeltaTime);
168 }
169
171 {
172 VoiceInterface->Tick(DeltaTime);
173 }
174
175 return true;
176}
177
178#ifdef UE_EDITOR
179void static PrintToScreen(int32 Key, float Time, FColor MessageColor, FString Message)
180{
181 if (GEngine)
182 GEngine->AddOnScreenDebugMessage(Key, Time, MessageColor, Message);
183}
184#endif
185
187{
188 bool bCouldLoad = false;
189 FString SubsystemFilePath = FPaths::Combine(FPaths::ProjectPersistentDownloadDir(), TEXT("pixo_onlinesubsystem.ini"));
190 Log("Trying to parse subsystem file: %s", *SubsystemFilePath);
191 IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
192 if (FileManager.FileExists(*SubsystemFilePath))
193 {
194 FString SubsystemConfigContent;
195 if (FFileHelper::LoadFileToString(SubsystemConfigContent, *SubsystemFilePath))
196 {
197 Log("Successfully loaded file.");
198 if (!SubsystemConfigContent.IsEmpty())
199 {
200 TMap<FString, FString> ParsedConfig;
201 bCouldLoad = ParseConfig(SubsystemConfigContent, ParsedConfig);
202 if (bCouldLoad)
203 {
204 bool bIgnoreVersion = false;
205 if (ParsedConfig.Contains("ignoreVersion"))
206 {
207 bIgnoreVersion = ParsedConfig["ignoreVersion"].ToBool();
208 Log("%s configuration file version.", bIgnoreVersion ? TEXT("Will ignore") : TEXT("Will not ignore"));
209 }
210
211 if (ParsedConfig.Contains("version") && !bIgnoreVersion)
212 {
213 FString ConfigVersion = ParsedConfig["version"];
214 if (ConfigVersion.Compare(ModuleVersion))
215 {
216 Log("Versions are different. Not loading config.")
217 bCouldLoad = false;
218 }
219 }
220
221 if (ParsedConfig.Contains("url") && bCouldLoad)
222 {
223 MultiplayerURL = ParsedConfig["url"];
224 MultiplayerURL.RemoveFromEnd(TEXT("/"));
225 }
226 }
227 }
228 }
229 }
230 else
231 {
232 Log("No multiplayer subsystem config file exists at %s.", *SubsystemFilePath);
233 }
234
235 return bCouldLoad;
236}
237
238bool FPVROSubsystem::ParseConfig(FString ConfigContent, TMap<FString, FString>& ConfigValues)
239{
240 TArray<FString> ConfigLines;
241 ConfigContent.ParseIntoArrayLines(ConfigLines);
242
243
244 if (ConfigLines.Num() < 0)
245 {
246 return false;
247 }
248
249 FString Key, Value;
250 for (int ConfigIndex = 0; ConfigIndex < ConfigLines.Num(); ConfigIndex++)
251 {
252 if (ConfigLines[ConfigIndex].Split(TEXT("="), &Key, &Value))
253 {
254 ConfigValues.Add(Key.ToLower(), Value);
255 }
256 }
257
258 return true;
259}
260
262{
263 bool bCouldSave = true;
264 FString SubsystemFilePath = FPaths::Combine(FPaths::ProjectPersistentDownloadDir(), TEXT("pixo_onlinesubsystem.ini"));
265
266 FString ConfigContent = "url=" + MultiplayerURL + "\nversion=" + ModuleVersion;
267
268 if (!FFileHelper::SaveStringToFile(ConfigContent, *SubsystemFilePath))
269 {
270 bCouldSave = false;
271 Warn("Failed to save file %s", *SubsystemFilePath);
272 }
273
274 return bCouldSave;
275}
276
278{
279 const bool bPixoVRInit = true;
280
281 Log("Initializing the PVROSubsystem!");
282 if (bPixoVRInit)
283 {
284 FString SubsystemFilePath = FPaths::Combine(FPaths::ProjectPersistentDownloadDir(), TEXT("pixo_onlinesubsystem.ini"));
285 IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
286
287 if (!GConfig->GetString(TEXT("/Script/ApexSDK.ApexSDKSettings"), TEXT("ModuleVersion"), ModuleVersion, GEngineIni))
288 {
289 int MajorVersion = 0, MinorVersion = 0, PatchVersion = 0;
290 GConfig->GetInt(TEXT("/Script/ApexSDK.ApexSDKSettings"), TEXT("MajorVerison"), MajorVersion, GEngineIni);
291 GConfig->GetInt(TEXT("/Script/ApexSDK.ApexSDKSettings"), TEXT("MinorVersion"), MinorVersion, GEngineIni);
292 GConfig->GetInt(TEXT("/Script/ApexSDK.ApexSDKSettings"), TEXT("PatchVersion"), PatchVersion, GEngineIni);
293 ModuleVersion = FString::Printf(TEXT("%i.%.2i.%.2i"), MajorVersion, MinorVersion, PatchVersion);
294 }
295
296 GConfig->GetInt(TEXT("/Script/ApexSDK.ApexSDKSettings"), TEXT("ModuleId"), ModuleId, GEngineIni);
297
298 if (!TryLoadConfig())
299 {
300 Log("Config failed to load, defaulting to variables in config.");
301 if (!GConfig->GetString(TEXT("PixoVRMultiplayer"), TEXT("ServerURI"), MultiplayerURL, GGameIni))
302 {
303 Log("Could not find configuration value in the game ini, checking engine ini as a back up.");
304 if (!GConfig->GetString(TEXT("PixoVRMultiplayer"), TEXT("ServerURI"), MultiplayerURL, GEngineIni))
305 {
306 Warn("Failed to get the Server URI, falling back to 127.0.0.1.");
307 MultiplayerURL = "127.0.0.1";
308 }
309 }
310
311 Warn("Multiplayer url is %s.", *MultiplayerURL);
312
313 if (!MultiplayerURL.IsEmpty())
314 {
315 FString ConnectionString = "wss://";
316 if (MultiplayerURL.Contains(TEXT("ws://")))
317 {
318 ConnectionString = "ws://";
319 Warn("This looks to be a normal web socket. Make sure you use this only for testing.");
320 }
321
322 MultiplayerURL.RemoveFromStart(ConnectionString);
323 MultiplayerURL.RemoveFromStart(TEXT("https://"));
324 MultiplayerURL.RemoveFromEnd(TEXT("/"));
325
326 MultiplayerURL = ConnectionString + MultiplayerURL;
327 SaveConfig();
328 }
329 }
330
333 Log("Server URL is %s", *MultiplayerURL);
334
336 check(TaskThreadManager);
337 TaskThread = FRunnableThread::Create(TaskThreadManager, *FString::Printf(TEXT("Task Manager %s(%d)"), *InstanceName.ToString(), TaskCounter.Increment()), 128 * 1024, TPri_Normal);
338 check(TaskThread);
339 Verbose("Created thread (ID:%d).", TaskThread->GetThreadID());
340
341 SessionInterface = MakeShareable(new FPVROnlineSession(this));
342 IdentityInterface = MakeShareable(new FPVROIdentity(this));
343 VoiceInterface = MakeShareable(new FOnlineVoiceImpl(this));
344
345#if !UE_SERVER
346 WebSocket.OnWebSocketConnected().AddLambda([&]() -> void { Log("Web socket connected!"); });
347 WebSocket.OnWebSocketConnectFailed().AddLambda([&](const FString& Error) -> void { Warn("Web socket failed to connect because %s", *Error); });
348 WebSocket.OnWebSocketDisconnected().AddLambda([&](int32 StatusCode, const FString& Reason, bool bWasClean) ->
349 void { Warn("Web socket disconnected %s w/ status code %d for reason %s", bWasClean ? "cleanly" : "not cleanly", StatusCode, *Reason); });
350#endif
351 }
352 else
353 {
354 Shutdown();
355 }
356
357 return bPixoVRInit;
358}
359
361{
362#if UE_SERVER
363 return false;
364#else
365 Warn("Multiplayer url is %s.", *MultiplayerURL);
367#endif
368}
369
371{
372 FOnlineSubsystemImpl::Shutdown();
373
375
376 if (TaskThread)
377 {
378 // Destroy the online async task thread
379 delete TaskThread;
380 TaskThread = nullptr;
381 }
382
383 if (TaskThread)
384 {
385 delete TaskThread;
386 TaskThread = nullptr;
387 }
388
390 {
391 VoiceInterface->Shutdown();
392 }
393
394 if (SessionInterface.IsValid())
395 {
396 SessionInterface->CancelFindSessions();
397 }
398
399#define DESTRUCT_INTERFACE(Interface) \
400 if (Interface.IsValid()) \
401 { \
402 ensure(Interface.IsUnique()); \
403 Interface = nullptr; \
404 }
405
406 // Destruct the interfaces
409
410#undef DESTRUCT_INTERFACE
411
412 return true;
413}
414
416{
417 return TEXT("");
418}
419
420bool FPVROSubsystem::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
421{
422 if (FOnlineSubsystemImpl::Exec(InWorld, Cmd, Ar))
423 {
424 return true;
425 }
426 return false;
427}
429{
430 return NSLOCTEXT("PixoVRMultiplayer", "OnlineServiceName", "PixoVR");
431}
432
433void FPVROSubsystem::SetUserInfo(FString InUserName, int InUserOrgId)
434{
435 UserName = InUserName;
436 UserOrgId = InUserOrgId;
437 IdentityInterface->SetPlayerNickname(0, UserName);
438}
439
440void FPVROSubsystem::SetModuleInfo(int InModuleId, FString InModuleVersion)
441{
442 ModuleId = InModuleId;
443 ModuleVersion = InModuleVersion;
444}
445
446bool FPVROSubsystem::FindSessions(int InOrgId, int InModuleId, FString InUserName, FString InModuleVersion)
447{
448 if (InOrgId >= 0)
449 {
450 UserOrgId = InOrgId;
451 }
452
453 if (InModuleId >= 0)
454 {
455 ModuleId = InModuleId;
456 }
457
458 if (!InUserName.IsEmpty())
459 {
460 UserName = InUserName;
461 }
462
463 if (!InModuleVersion.IsEmpty())
464 {
465 ModuleVersion = InModuleVersion;
466 }
467
468 Log("Finding sessions for Module ID %d with Org ID %d.", UserOrgId, ModuleId);
469 if (UserOrgId < 0 || ModuleId < 0)
470 {
471 Error("Invalid OrgId or ModuleId used when finding a session.");
472 return false;
473 }
474
475#if UE_SERVER
476 return false;
477#else
478 bool bCanFindSession = true;
480 {
481 bCanFindSession = ConnectWebsocket();
482 }
483
484 if (bCanFindSession)
485 {
487 }
488
489 return bCanFindSession;
490#endif
491}
492
493#undef Fatal
494#undef Error
495#undef Warn
496#undef Log
497#undef Verbose
#define Error(pmt,...)
#define Warn(pmt,...)
#define Log(pmt,...)
#define Verbose(pmt,...)
#define DESTRUCT_INTERFACE(Interface)
DEFINE_LOG_CATEGORY_STATIC(LogPVROSubSystem, Log, All)
#define Log(pmt,...)
virtual bool Init() override
virtual FString GetAppId() const override
virtual IOnlineUserCloudPtr GetUserCloudInterface() const override
virtual IOnlineEventsPtr GetEventsInterface() const override
bool ParseConfig(FString ConfigContent, TMap< FString, FString > &ConfigValues)
class FRunnableThread * TaskThread
virtual IOnlineStatsPtr GetStatsInterface() const override
virtual FText GetOnlineServiceName() const override
virtual IOnlineAchievementsPtr GetAchievementsInterface() const override
bool FindSessions(int InOrgId=-1, int InModuleId=-1, FString InUserName="", FString InModuleVersion="")
void SetModuleInfo(int InModuleId, FString InModuleVersion)
virtual IOnlineEntitlementsPtr GetEntitlementsInterface() const override
virtual IOnlineSessionPtr GetSessionInterface() const override
FPVROnlineSessionPtr SessionInterface
virtual IOnlinePresencePtr GetPresenceInterface() const override
virtual IOnlineUserPtr GetUserInterface() const override
virtual IOnlineSharingPtr GetSharingInterface() const override
virtual IOnlineTurnBasedPtr GetTurnBasedInterface() const override
virtual IOnlineLeaderboardsPtr GetLeaderboardsInterface() const override
virtual IOnlineGroupsPtr GetGroupsInterface() const override
virtual IOnlineFriendsPtr GetFriendsInterface() const override
void QueueAsyncTask(class FOnlineAsyncTask *AsyncTask)
virtual bool Tick(float DeltaTime) override
FString ModuleVersion
virtual IOnlineTitleFilePtr GetTitleFileInterface() const override
bool bVoiceInterfaceInitialized
virtual IOnlineMessagePtr GetMessageInterface() const override
FString MultiplayerURL
virtual IOnlinePartyPtr GetPartyInterface() const override
virtual IOnlineSharedCloudPtr GetSharedCloudInterface() const override
void SetUserInfo(FString InUserName, int InUserOrgId)
MultiplayerWebSocket WebSocket
virtual IOnlineIdentityPtr GetIdentityInterface() const override
virtual IOnlineChatPtr GetChatInterface() const override
virtual bool Exec(class UWorld *InWorld, const TCHAR *Cmd, FOutputDevice &Ar) override
FOnlineVoiceImplPtr VoiceInterface
class FPVROAsyncTaskManager * TaskThreadManager
virtual IOnlineVoicePtr GetVoiceInterface() const override
virtual bool Shutdown() override
virtual IOnlineTimePtr GetTimeInterface() const override
virtual IOnlineTournamentPtr GetTournamentInterface() const override
virtual IOnlineExternalUIPtr GetExternalUIInterface() const override
static FThreadSafeCounter TaskCounter
FPVROIdentityPtr IdentityInterface
FSocketDisconnected & OnWebSocketDisconnected()
FSocketConnected & OnWebSocketConnected()
void RequestMatchMake(int OrgId, int ModuleId)
FSocketConnectFailed & OnWebSocketConnectFailed()
bool AttemptConnect(FString Uri, FString Protocol="wss")