Up to Unreal 5.7

This commit is contained in:
Simeon "Waldo" Wallrath 2025-11-20 16:26:22 +01:00
parent 8a63902c06
commit 1a64805d43
239 changed files with 3947 additions and 296 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "VIRTUOS_ExpansionPluginTests/Plugins/StevesUEHelpers"]
path = VIRTUOS_ExpansionPluginTests/Plugins/StevesUEHelpers
url = https://github.com/sinbad/StevesUEHelpers
[submodule "VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin"]
path = VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin
url = https://github.com/mordentral/VRExpansionPlugin

View file

@ -6,6 +6,8 @@
"Microsoft.Net.Component.4.6.2.TargetingPack",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL",
"Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64",
"Microsoft.VisualStudio.Component.VC.Llvm.Clang",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",

View file

@ -1,2 +0,0 @@
* text=auto
*.bat eol=crlf

View file

@ -1,10 +0,0 @@
.hg/
binaries/
deriveddatacache/
.vs/
build/
intermediate/
PACKPLUGIN/
saved/
*.orig

View file

@ -1,8 +1,8 @@
{
"FileVersion": 3,
"FriendlyName": "Advanced Sessions",
"Version": 5.4,
"VersionName": "5.4",
"Version": 5.7,
"VersionName": "5.7",
"Description": "Adds new blueprint functions to handle more advanced session operations.",
"Category": "Advanced Sessions Plugin",
"CreatedBy": "Joshua Statzer",

View file

@ -1,19 +0,0 @@
Copyright Joshua Statzer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,7 +0,0 @@
### How do I use it? ###
**KantanDocGen Automatic Documentation ([KantanDocGen](http://kantandev.com/free/kantan-doc-gen))**
**[AdvancedSessions](https://vreue4.com/generated-node-documentation?section=advanced-sessions-plugin)**
**[AdvancedSteamSessions](https://vreue4.com/generated-node-documentation?section=advanced-steam-sessions-plugin)**

View file

@ -5,6 +5,9 @@ public class AdvancedSessions : ModuleRules
{
public AdvancedSessions(ReadOnlyTargetRules Target) : base(Target)
{
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
//bEnforceIWYU = true;

View file

@ -44,6 +44,16 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedVoiceInterface)
bool bEnableTalkingStatusDelegate;
// If true we will auto join a session we have accepted in the overlay.
// This can get in the way of Beacon Sessions, you may want to disable it.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
bool bAutoJoinSessionOnAcceptedUserInviteReceived = false;
// If true we will auto travel to a game session when an invite is received.
// This can get in the way of Beacon Sessions, you may want to disable it.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
bool bAutoTravelOnAcceptedUserInviteReceived = false;
//virtual void PostLoad() override;
virtual void Shutdown() override;
virtual void Init() override;
@ -52,6 +62,13 @@ public:
FOnSessionInviteReceivedDelegate SessionInviteReceivedDelegate;
FDelegateHandle SessionInviteReceivedDelegateHandle;
// custom handle to join directly from steam ui "Join Game"
FDelegateHandle OnJoinSessionCompleteDelegateHandle;
// custom Steam UI Join User function #Self invite#
void OnSessionUserInviteAccepted(const bool bWasSuccessful, const int32 ControllerId, FUniqueNetIdPtr UserId, const FOnlineSessionSearchResult& InviteResult);
// custom Steam UI function to client travel #Self invite#
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
//const FUniqueNetId& /*UserId*/, const FUniqueNetId& /*FromId*/, const FString& /*AppId*/, const FOnlineSessionSearchResult& /*InviteResult*/
void OnSessionInviteReceivedMaster(const FUniqueNetId & PersonInvited, const FUniqueNetId & PersonInviting, const FString & AppId, const FOnlineSessionSearchResult& SessionToJoin);

View file

@ -160,6 +160,10 @@ public:
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static void GetUniqueNetID(APlayerController *PlayerController, FBPUniqueNetId &UniqueNetId);
// Get the unique net id of a network player attached to the given controller
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static void GetUniqueNetIdOfSessionOwner(FBlueprintSessionResult SessionResult, FBPUniqueNetId& UniqueNetId);
// Get the unique net id of a network player who is assigned the the given player state
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static void GetUniqueNetIDFromPlayerState(APlayerState *PlayerState, FBPUniqueNetId &UniqueNetId);

View file

@ -23,14 +23,14 @@ class UCreateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
* Creates a session with the default online subsystem with advanced optional inputs, for dedicated servers leave UsePresence as false and set IsDedicatedServer to true. Dedicated servers don't use presence.
* @param PublicConnections When doing a 'listen' server, this must be >=2 (ListenServer itself counts as a connection)
* @param bUseLAN When you want to play LAN, the level to play on must be loaded with option 'bIsLanMatch'
* @param bUsePresence Must be true for a 'listen' server (Map must be loaded with option 'listen'), false for a 'dedicated' server.
* @param bUseLobbiesIfAvailable Used to flag the subsystem to use a lobby api instead of general hosting if the API supports it, generally true on steam for listen servers and false for dedicated
* Must be true for a 'listen' server (Map must be loaded with option 'listen'), false for a 'dedicated' server.
* @param bShouldAdvertise Set to true when the OnlineSubsystem should list your server when someone is searching for servers. Otherwise the server is hidden and only join via invite is possible.
* @param bUseLobbiesVoiceChatIfAvailable Set to true to setup voice chat lobbies if the API supports it
* @param bStartAfterCreate Set to true to start the session after it's created. If false you need to manually call StartSession when ready.
*/
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject",AutoCreateRefTerm="ExtraSettings"), Category = "Online|AdvancedSessions")
static UCreateSessionCallbackProxyAdvanced* CreateAdvancedSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair>& ExtraSettings, class APlayerController* PlayerController = NULL, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = true, bool bIsDedicatedServer = false, bool bUsePresence = true, bool bUseLobbiesIfAvailable = true, bool bAllowJoinViaPresence = true, bool bAllowJoinViaPresenceFriendsOnly = false, bool bAntiCheatProtected = false, bool bUsesStats = false, bool bShouldAdvertise = true, bool bUseLobbiesVoiceChatIfAvailable = false, bool bStartAfterCreate = true);
static UCreateSessionCallbackProxyAdvanced* CreateAdvancedSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair>& ExtraSettings, class APlayerController* PlayerController = NULL, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = true, bool bIsDedicatedServer = false, /*bool bUsePresence = true,*/ bool bUseLobbiesIfAvailable = true, bool bAllowJoinViaPresence = true, bool bAllowJoinViaPresenceFriendsOnly = false, bool bAntiCheatProtected = false, bool bUsesStats = false, bool bShouldAdvertise = true, bool bUseLobbiesVoiceChatIfAvailable = false, bool bStartAfterCreate = true);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;

View file

@ -28,7 +28,7 @@ class UFindSessionsCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
// Searches for advertised sessions with the default online subsystem and includes an array of filters
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", AutoCreateRefTerm="Filters"), Category = "Online|AdvancedSessions")
static UFindSessionsCallbackProxyAdvanced* FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int32 MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray<FSessionsSearchSetting> &Filters, bool bEmptyServersOnly = false, bool bNonEmptyServersOnly = false, bool bSecureServersOnly = false, bool bSearchLobbies = true, int MinSlotsAvailable = 0);
static UFindSessionsCallbackProxyAdvanced* FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int32 MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray<FSessionsSearchSetting> &Filters, bool bEmptyServersOnly = false, bool bNonEmptyServersOnly = false, bool bSecureServersOnly = false, /*bool bSearchLobbies = true,*/ int MinSlotsAvailable = 0);
static bool CompareVariants(const FVariantData &A, const FVariantData &B, EOnlineComparisonOpRedux Comparator);
@ -99,7 +99,7 @@ private:
bool bSecureServersOnly;
// Search through lobbies
bool bSearchLobbies;
//bool bSearchLobbies;
// Min slots requires to search
int MinSlotsAvailable;

View file

@ -21,7 +21,7 @@ class UUpdateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
// Creates a session with the default online subsystem with advanced optional inputs, you MUST fill in all categories or it will pass in values that you didn't want as default values
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject",AutoCreateRefTerm="ExtraSettings"), Category = "Online|AdvancedSessions")
static UUpdateSessionCallbackProxyAdvanced* UpdateSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair> &ExtraSettings, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = false, bool bAllowJoinInProgress = false, bool bRefreshOnlineData = true, bool bIsDedicatedServer = false, bool bShouldAdvertise = true);
static UUpdateSessionCallbackProxyAdvanced* UpdateSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair> &ExtraSettings, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = false, bool bAllowJoinInProgress = false, bool bRefreshOnlineData = true, bool bIsDedicatedServer = false, bool bShouldAdvertise = true, bool bAllowJoinViaPresence = true, bool bAllowJoinViaPresenceFriendsOnly = false);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
@ -58,6 +58,12 @@ private:
// Allow joining in progress
bool bAllowJoinInProgress = true;
// Allow joining in progress
bool bAllowJoinViaPresence = true;
// Allow joining in progress
bool bAllowJoinViaPresenceFriendsOnly = false;
// Update whether this is a dedicated server or not
bool bDedicatedServer = false;

View file

@ -20,6 +20,61 @@ UAdvancedFriendsGameInstance::UAdvancedFriendsGameInstance(const FObjectInitiali
{
}
void UAdvancedFriendsGameInstance::OnSessionUserInviteAccepted(const bool bWasSuccessful, const int32 ControllerId, FUniqueNetIdPtr UserId, const FOnlineSessionSearchResult& InviteResult)
{
if (!bAutoJoinSessionOnAcceptedUserInviteReceived)
return;
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());
if (SessionInterface.IsValid())
{
// Eventually call this?, would need another call back to run through
//SessionInterface->DestroySession(NAME_GameSession);
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);
OnJoinSessionCompleteDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(
FOnJoinSessionCompleteDelegate::CreateUObject(this, &UAdvancedFriendsGameInstance::OnJoinSessionComplete));
// Temp for 5.5, they aren't filling in the struct correctly
if (!InviteResult.Session.SessionSettings.bIsDedicated)
{
FOnlineSessionSearchResult ModResult = InviteResult;
ModResult.Session.SessionSettings.bUsesPresence = true;
ModResult.Session.SessionSettings.bUseLobbiesIfAvailable = true;
SessionInterface->JoinSession(0, NAME_GameSession, ModResult);
}
else
{
SessionInterface->JoinSession(0, NAME_GameSession, InviteResult);
}
}
UE_LOG(AdvancedFriendsInterfaceLog, Log, TEXT("Called Join Session for Steam Friends List UI InviteResults: %s, UserId: %s"), *InviteResult.GetSessionIdStr(), *UserId->ToString());
}
void UAdvancedFriendsGameInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
// If we don't want to auto travel to the session instance then exit out
if (!bAutoTravelOnAcceptedUserInviteReceived)
{
return;
}
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());
if (SessionInterface.IsValid())
{
FString ConnectInfo;
if (SessionInterface->GetResolvedConnectString(NAME_GameSession, ConnectInfo))
{
APlayerController* PlayerController = GetFirstLocalPlayerController();
if (PlayerController)
{
PlayerController->ClientTravel(ConnectInfo, ETravelType::TRAVEL_Absolute);
}
}
}
}
void UAdvancedFriendsGameInstance::Shutdown()
{
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());
@ -34,6 +89,7 @@ void UAdvancedFriendsGameInstance::Shutdown()
// Clear all of the delegate handles here
SessionInterface->ClearOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegateHandle);
SessionInterface->ClearOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegateHandle);
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);
}
@ -80,6 +136,9 @@ void UAdvancedFriendsGameInstance::Init()
SessionInviteAcceptedDelegateHandle = SessionInterface->AddOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegate);
SessionInviteReceivedDelegateHandle = SessionInterface->AddOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegate);
// Custom steam join game delegate
SessionInterface->OnSessionUserInviteAcceptedDelegates.AddUObject(this, &UAdvancedFriendsGameInstance::OnSessionUserInviteAccepted);
}
else
{
@ -271,6 +330,13 @@ void UAdvancedFriendsGameInstance::OnSessionInviteReceivedMaster(const FUniqueNe
}
}
// Temp for 5.5, they aren't filling in the struct correctly
if (!BluePrintResult.OnlineResult.Session.SessionSettings.bIsDedicated)
{
BluePrintResult.OnlineResult.Session.SessionSettings.bUsesPresence = true;
BluePrintResult.OnlineResult.Session.SessionSettings.bUseLobbiesIfAvailable = true;
}
OnSessionInviteReceived(LocalPlayer, PInviting, AppId, BluePrintResult);
//IAdvancedFriendsInterface* TheInterface = NULL;
@ -307,6 +373,13 @@ void UAdvancedFriendsGameInstance::OnSessionInviteAcceptedMaster(const bool bWas
FBPUniqueNetId PInvited;
PInvited.SetUniqueNetId(PersonInvited);
// Temp for 5.5, they aren't filling in the struct correctly
if (!BluePrintResult.OnlineResult.Session.SessionSettings.bIsDedicated)
{
BluePrintResult.OnlineResult.Session.SessionSettings.bUsesPresence = true;
BluePrintResult.OnlineResult.Session.SessionSettings.bUseLobbiesIfAvailable = true;
}
OnSessionInviteAccepted(LocalPlayer,PInvited, BluePrintResult);
APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), LocalPlayer);

View file

@ -151,7 +151,9 @@ void UAdvancedFriendsLibrary::GetFriend(APlayerController *PlayerController, con
Friend.PresenceInfo.bIsPlaying = pres.bIsPlaying;
Friend.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;
Friend.PresenceInfo.PresenceState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
// #TODO: Check back in on this in shipping, epic is missing the UTF8_TO_TCHAR call on converting this and its making an invalid string
// OnlineFriendPresenceInterfaceSteam has the issue
//Friend.PresenceInfo.StatusString = pres.Status.StatusStr;
}
}
@ -266,6 +268,7 @@ void UAdvancedFriendsLibrary::GetStoredFriendsList(APlayerController *PlayerCont
BPF.PresenceInfo.bIsPlaying = pres.bIsPlaying;
BPF.PresenceInfo.PresenceState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
// #TODO: Check back in on this in shipping, epic is missing the UTF8_TO_TCHAR call on converting this and its making an invalid string
// OnlineFriendPresenceInterfaceSteam has the issue
//BPF.PresenceInfo.StatusString = pres.Status.StatusStr;
BPF.PresenceInfo.bIsJoinable = pres.bIsJoinable;
BPF.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;

View file

@ -451,6 +451,17 @@ void UAdvancedSessionsLibrary::GetUniqueNetID(APlayerController *PlayerControlle
}
}
void UAdvancedSessionsLibrary::GetUniqueNetIdOfSessionOwner(FBlueprintSessionResult SessionResult, FBPUniqueNetId& UniqueNetId)
{
FBPUniqueNetId ReturnID;
if (SessionResult.OnlineResult.IsValid())
{
ReturnID.SetUniqueNetId(SessionResult.OnlineResult.Session.OwningUserId);
}
UniqueNetId = ReturnID;
}
void UAdvancedSessionsLibrary::GetUniqueNetIDFromPlayerState(APlayerState *PlayerState, FBPUniqueNetId &UniqueNetId)
{
if (!PlayerState)

View file

@ -83,6 +83,8 @@ void UAutoLoginUserCallbackProxy::OnCompleted(int32 LocalUserNum, bool bWasSucce
{
OnFailure.Broadcast();
}
return;
}
OnFailure.Broadcast();

View file

@ -13,7 +13,7 @@ UCreateSessionCallbackProxyAdvanced::UCreateSessionCallbackProxyAdvanced(const F
{
}
UCreateSessionCallbackProxyAdvanced* UCreateSessionCallbackProxyAdvanced::CreateAdvancedSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair>& ExtraSettings, class APlayerController* PlayerController, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bIsDedicatedServer, bool bUsePresence, bool bUseLobbiesIfAvailable, bool bAllowJoinViaPresence, bool bAllowJoinViaPresenceFriendsOnly, bool bAntiCheatProtected, bool bUsesStats, bool bShouldAdvertise, bool bUseLobbiesVoiceChatIfAvailable, bool bStartAfterCreate)
UCreateSessionCallbackProxyAdvanced* UCreateSessionCallbackProxyAdvanced::CreateAdvancedSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair>& ExtraSettings, class APlayerController* PlayerController, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bIsDedicatedServer, /*bool bUsePresence,*/ bool bUseLobbiesIfAvailable, bool bAllowJoinViaPresence, bool bAllowJoinViaPresenceFriendsOnly, bool bAntiCheatProtected, bool bUsesStats, bool bShouldAdvertise, bool bUseLobbiesVoiceChatIfAvailable, bool bStartAfterCreate)
{
UCreateSessionCallbackProxyAdvanced* Proxy = NewObject<UCreateSessionCallbackProxyAdvanced>();
Proxy->PlayerControllerWeakPtr = PlayerController;
@ -24,7 +24,7 @@ UCreateSessionCallbackProxyAdvanced* UCreateSessionCallbackProxyAdvanced::Create
Proxy->bAllowInvites = bAllowInvites;
Proxy->ExtraSettings = ExtraSettings;
Proxy->bDedicatedServer = bIsDedicatedServer;
Proxy->bUsePresence = bUsePresence;
/*Proxy->bUsePresence = bUsePresence;*/
Proxy->bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
Proxy->bAllowJoinViaPresence = bAllowJoinViaPresence;
Proxy->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
@ -61,13 +61,13 @@ void UCreateSessionCallbackProxyAdvanced::Activate()
if (bDedicatedServer)
{
Settings.bUsesPresence = false;
Settings.bUseLobbiesIfAvailable = false;
Settings.bUsesPresence = false;
}
else
{
Settings.bUsesPresence = bUsePresence;
Settings.bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
Settings.bUsesPresence = bUseLobbiesIfAvailable;
}
Settings.bUseLobbiesVoiceChatIfAvailable = bUseLobbiesIfAvailable ? bUseLobbiesVoiceChatIfAvailable : false;

View file

@ -102,6 +102,14 @@ void UFindFriendSessionCallbackProxy::OnFindFriendSessionCompleted(int32 LocalPl
{
FBlueprintSessionResult BSesh;
BSesh.OnlineResult = Sesh;
// Temp for 5.5, force the values if epic isn't setting them, lobbies should always have these true
if (!BSesh.OnlineResult.Session.SessionSettings.bIsDedicated)
{
BSesh.OnlineResult.Session.SessionSettings.bUseLobbiesIfAvailable = true;
BSesh.OnlineResult.Session.SessionSettings.bUsesPresence = true;
}
Result.Add(BSesh);
}
}

View file

@ -16,7 +16,7 @@ UFindSessionsCallbackProxyAdvanced::UFindSessionsCallbackProxyAdvanced(const FOb
bIsOnSecondSearch = false;
}
UFindSessionsCallbackProxyAdvanced* UFindSessionsCallbackProxyAdvanced::FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray<FSessionsSearchSetting> &Filters, bool bEmptyServersOnly, bool bNonEmptyServersOnly, bool bSecureServersOnly, bool bSearchLobbies, int MinSlotsAvailable)
UFindSessionsCallbackProxyAdvanced* UFindSessionsCallbackProxyAdvanced::FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray<FSessionsSearchSetting> &Filters, bool bEmptyServersOnly, bool bNonEmptyServersOnly, bool bSecureServersOnly, /*bool bSearchLobbies,*/ int MinSlotsAvailable)
{
UFindSessionsCallbackProxyAdvanced* Proxy = NewObject<UFindSessionsCallbackProxyAdvanced>();
Proxy->PlayerControllerWeakPtr = PlayerController;
@ -28,7 +28,7 @@ UFindSessionsCallbackProxyAdvanced* UFindSessionsCallbackProxyAdvanced::FindSess
Proxy->bEmptyServersOnly = bEmptyServersOnly,
Proxy->bNonEmptyServersOnly = bNonEmptyServersOnly;
Proxy->bSecureServersOnly = bSecureServersOnly;
Proxy->bSearchLobbies = bSearchLobbies;
//Proxy->bSearchLobbies = bSearchLobbies;
Proxy->MinSlotsAvailable = MinSlotsAvailable;
return Proxy;
}
@ -86,7 +86,6 @@ void UFindSessionsCallbackProxyAdvanced::Activate()
/** #define SEARCH_SWITCH_SELECTION_METHOD FName(TEXT("SWITCHSELECTIONMETHOD"))*/
/** Whether to use lobbies vs sessions */
/** #define SEARCH_LOBBIES FName(TEXT("LOBBYSEARCH"))*/
if (bEmptyServersOnly)
tem.Set(SEARCH_EMPTY_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
@ -114,9 +113,9 @@ void UFindSessionsCallbackProxyAdvanced::Activate()
case EBPServerPresenceSearchType::ClientServersOnly:
{
tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
//tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
if (bSearchLobbies && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
//if (bSearchLobbies)// && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
}
break;
@ -140,9 +139,9 @@ void UFindSessionsCallbackProxyAdvanced::Activate()
FOnlineSearchSettingsEx DedicatedOnly = tem;
tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
//tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
if (bSearchLobbies && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
//if (bSearchLobbies)// && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
//DedicatedOnly.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals);
@ -225,6 +224,14 @@ void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess)
FBlueprintSessionResult BPResult;
BPResult.OnlineResult = Result;
// Temp for 5.5, force the values if epic isn't setting them, lobbies should always have these true
if (ServerSearchType != EBPServerPresenceSearchType::DedicatedServersOnly )
{
BPResult.OnlineResult.Session.SessionSettings.bUseLobbiesIfAvailable = true;
BPResult.OnlineResult.Session.SessionSettings.bUsesPresence = true;
}
SessionSearchResults.AddUnique(BPResult);
}
if (!bRunSecondSearch)

View file

@ -12,7 +12,7 @@ UUpdateSessionCallbackProxyAdvanced::UUpdateSessionCallbackProxyAdvanced(const F
{
}
UUpdateSessionCallbackProxyAdvanced* UUpdateSessionCallbackProxyAdvanced::UpdateSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair> &ExtraSettings, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bAllowJoinInProgress, bool bRefreshOnlineData, bool bIsDedicatedServer, bool bShouldAdvertise)
UUpdateSessionCallbackProxyAdvanced* UUpdateSessionCallbackProxyAdvanced::UpdateSession(UObject* WorldContextObject, const TArray<FSessionPropertyKeyPair> &ExtraSettings, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bAllowJoinInProgress, bool bRefreshOnlineData, bool bIsDedicatedServer, bool bShouldAdvertise, bool bAllowJoinViaPresence, bool bAllowJoinViaPresenceFriendsOnly)
{
UUpdateSessionCallbackProxyAdvanced* Proxy = NewObject<UUpdateSessionCallbackProxyAdvanced>();
Proxy->NumPublicConnections = PublicConnections;
@ -25,6 +25,8 @@ UUpdateSessionCallbackProxyAdvanced* UUpdateSessionCallbackProxyAdvanced::Update
Proxy->bAllowJoinInProgress = bAllowJoinInProgress;
Proxy->bDedicatedServer = bIsDedicatedServer;
Proxy->bShouldAdvertise = bShouldAdvertise;
Proxy->bAllowJoinViaPresence = bAllowJoinViaPresence;
Proxy->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
return Proxy;
}
@ -65,11 +67,14 @@ void UUpdateSessionCallbackProxyAdvanced::Activate()
Settings->bAllowJoinInProgress = bAllowJoinInProgress;
Settings->bIsLANMatch = bUseLAN;
//Settings->bUsesPresence = true;
//Settings->bAllowJoinViaPresence = true;
Settings->bAllowInvites = bAllowInvites;
Settings->bAllowJoinInProgress = bAllowJoinInProgress;
Settings->bIsDedicated = bDedicatedServer;
// Added in 5.6
Settings->bAllowJoinViaPresence = bAllowJoinViaPresence;
Settings->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
FOnlineSessionSetting * fSetting = NULL;
FOnlineSessionSetting ExtraSetting;
for (int i = 0; i < ExtraSettings.Num(); i++)

View file

@ -2,8 +2,8 @@
"FileVersion" : 3,
"FriendlyName" : "Advanced Steam Sessions",
"Version" : 5.4,
"VersionName": "5.4",
"Version" : 5.7,
"VersionName": "5.7",
"Description" : "Adds new blueprint functions to handle more advanced session operations in Steam. REQUIRES ADVANCED SESSIONS",
"Category" : "Advanced Sessions Plugin",
"CreatedBy" : "Joshua Statzer",
@ -14,7 +14,7 @@
{
"Name": "AdvancedSteamSessions",
"Type": "RunTime",
"LoadingPhase": "Default"
"LoadingPhase": "PostDefault"
}
],
"Plugins": [

View file

@ -5,6 +5,9 @@ public class AdvancedSteamSessions : ModuleRules
{
public AdvancedSteamSessions(ReadOnlyTargetRules Target) : base(Target)
{
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
//bEnforceIWYU = true;
@ -16,6 +19,7 @@ public class AdvancedSteamSessions : ModuleRules
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Linux) || (Target.Platform == UnrealTargetPlatform.Mac))
{
PublicDependencyModuleNames.AddRange(new string[] { "SteamShared", "Steamworks", "OnlineSubsystemSteam" });
AddEngineThirdPartyPrivateStaticDependencies(Target, "Steamworks");
//PublicIncludePaths.AddRange(new string[] { "../Plugins/Online/OnlineSubsystemSteam/Source/Private" });// This is dumb but it isn't very open
}
}

View file

@ -42,7 +42,7 @@ MSVC_PRAGMA(warning(pop))
#endif // USING_CODE_ANALYSIS
#include <steam/isteamapps.h>
#include <steam/isteamapplist.h>
//#include <steam/isteamapplist.h>
//#include <OnlineSubsystemSteamTypes.h>
#pragma pop_macro("ARRAY_COUNT")
@ -337,8 +337,8 @@ public:
static bool RequestSteamFriendInfo(const FBPUniqueNetId UniqueNetId, bool bRequireNameOnly = false);
// Opens the steam overlay to go to the specified user dialog
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
static bool OpenSteamUserOverlay(const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType);
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI", meta = (WorldContext = "WorldContextObject"))
static bool OpenSteamUserOverlay(UObject* WorldContextObject, const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType);
// Returns if the steam overlay is currently active (this can return false during initial overlay hooking)
UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")

View file

@ -38,14 +38,27 @@ public:
void Initialize(USteamNotificationsSubsystem* MyParent)
{
ParentSubsystem = MyParent;
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
OnExternalUITriggeredCallback.Register(this, &USteamNotificationsSubsystem::cSteamEventsStore::OnExternalUITriggered);
#endif
}
void UnInitialize(USteamNotificationsSubsystem* MyParent)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
OnExternalUITriggeredCallback.Unregister();
#endif
}
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
cSteamEventsStore() :
OnExternalUITriggeredCallback(this, &cSteamEventsStore::OnExternalUITriggered)
cSteamEventsStore()
{}
//:
/*OnExternalUITriggeredCallback(this, &cSteamEventsStore::OnExternalUITriggered)
{
}
}*/
#else
//cSteamEventsStore()
//{
@ -57,7 +70,8 @@ public:
private:
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
STEAM_CALLBACK(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
//STEAM_CALLBACK(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
STEAM_CALLBACK_MANUAL(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
#endif
};
@ -72,7 +86,7 @@ public:
/** Implement this for deinitialization of instances of the system */
virtual void Deinitialize() override
{
MyEvents.UnInitialize(this);
}
};

View file

@ -1,6 +1,11 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedSteamFriendsLibrary.h"
#include "OnlineSubSystemHeader.h"
#include "OnlineSubsystemTypes.h"
#include "Engine/Texture.h"
#include "Engine/Texture2D.h"
#include "TextureResource.h"
#include "PixelFormat.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedSteamFriendsLog);
@ -233,7 +238,7 @@ bool UAdvancedSteamFriendsLibrary::RequestSteamFriendInfo(const FBPUniqueNetId U
}
bool UAdvancedSteamFriendsLibrary::OpenSteamUserOverlay(const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType)
bool UAdvancedSteamFriendsLibrary::OpenSteamUserOverlay(UObject* WorldContextObject,const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
@ -244,13 +249,24 @@ bool UAdvancedSteamFriendsLibrary::OpenSteamUserOverlay(const FBPUniqueNetId Uni
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
if (DialogType == ESteamUserOverlayType::invitetolobby)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
if (SessionInterface.IsValid())
{
FNamedOnlineSession* CurrentSession = SessionInterface->GetNamedSession(NAME_GameSession);
if (CurrentSession && CurrentSession->SessionInfo->GetSessionId().IsValid())
{
uint64 id = *((uint64*)CurrentSession->SessionInfo->GetSessionId().GetBytes());
SteamFriends()->ActivateGameOverlayInviteDialog(id);
}
}
}
else
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
FString DialogName = EnumToString("ESteamUserOverlayType", (uint8)DialogType);
SteamFriends()->ActivateGameOverlayToUser(TCHAR_TO_ANSI(*DialogName), id);
}

View file

@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 5.5,
"VersionName": "5.5",
"Version": 5.7,
"VersionName": "5.7",
"FriendlyName": "OpenXRExpansionPlugin",
"Description": "An set of utility functions for OpenXR",
"Category": "Virtual Reality",

View file

@ -163,34 +163,40 @@ void UOpenXRExpansionFunctionLibrary::GetXRMotionControllerType(FString& Trackin
bool UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose)
{
FXRMotionControllerData MotionControllerData;
FXRHandTrackingState HandTrackingData;
FXRMotionControllerState MotionControllerData;
if (bGetMockUpPose)
{
GetMockUpControllerData(MotionControllerData, HandPoseContainer);
// #TODO 5.7 TEST THIS
UHeadMountedDisplayFunctionLibrary::GetMotionControllerState((UObject*)HandPoseComponent, EXRSpaceType::UnrealWorldSpace, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, HandPoseContainer.PoseType, MotionControllerData);
GetMockUpControllerData(HandTrackingData,MotionControllerData, HandPoseContainer);
return true;
}
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData((UObject*)HandPoseComponent, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, MotionControllerData);
UHeadMountedDisplayFunctionLibrary::GetHandTrackingState((UObject*)HandPoseComponent, EXRSpaceType::UnrealWorldSpace, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, HandTrackingData);
if (MotionControllerData.bValid)
if (HandTrackingData.bValid)
{
HandPoseContainer.SkeletalTransforms.Empty(MotionControllerData.HandKeyPositions.Num());
HandPoseContainer.SkeletalTransforms.Empty(HandTrackingData.HandKeyLocations.Num());
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
// #TODO: 5.7 Preview!!
// Might need to restore all of this, have to test with vive
/*if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
else // EXRVisualType::Hand visual type*/
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
ParentTrans = FTransform(HandTrackingData.HandKeyRotations[(uint8)EHandKeypoint::Palm], HandTrackingData.HandKeyLocations[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
for (int i = 0; i < HandTrackingData.HandKeyLocations.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
HandPoseContainer.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
HandPoseContainer.SkeletalTransforms.Add(FTransform(HandTrackingData.HandKeyRotations[i].GetNormalized(), HandTrackingData.HandKeyLocations[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
//if (bGetCurlValues)
@ -233,31 +239,33 @@ bool UOpenXRExpansionFunctionLibrary::GetOpenXRFingerCurlValuesForHand(
float& RingCurl,
float& PinkyCurl)
{
FXRMotionControllerData MotionControllerData;
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData(WorldContextObject, TargetHand, MotionControllerData);
FXRHandTrackingState HandTrackingData;
UHeadMountedDisplayFunctionLibrary::GetHandTrackingState(WorldContextObject, EXRSpaceType::UnrealWorldSpace, TargetHand, HandTrackingData);
// Fail if the count is too low
if (MotionControllerData.HandKeyPositions.Num() < EHandKeypointCount)
if (HandTrackingData.HandKeyLocations.Num() < EHandKeypointCount)
return false;
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
// #TODO: 5.7
// TEST THIS and restore if we need too
/*if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
else // EXRVisualType::Hand visual type*/
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
ParentTrans = FTransform(HandTrackingData.HandKeyRotations[(uint8)EHandKeypoint::Palm], HandTrackingData.HandKeyLocations[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
TArray<FTransform> TransformArray;
TransformArray.AddUninitialized(MotionControllerData.HandKeyPositions.Num());
TransformArray.AddUninitialized(HandTrackingData.HandKeyLocations.Num());
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
for (int i = 0; i < HandTrackingData.HandKeyLocations.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
TransformArray[i] = FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
TransformArray[i] = FTransform(HandTrackingData.HandKeyRotations[i].GetNormalized(), HandTrackingData.HandKeyLocations[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
}
ThumbCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
@ -436,7 +444,7 @@ void UOpenXRExpansionFunctionLibrary::ConvertHandTransformsSpaceAndBack(TArray<F
}
}
void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRHandTrackingState& HandTrackingData, FXRMotionControllerState& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
{
@ -499,7 +507,7 @@ void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControlle
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f)
};
MotionControllerData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
HandTrackingData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
TArray<FVector> HandPositionsClosed = {
// Closed palm - Left
@ -561,9 +569,9 @@ void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControlle
FVector(-1019.778f,-479.842f,203.819f)
};
MotionControllerData.HandKeyPositions = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
HandTrackingData.HandKeyLocations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
if (SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left)
/*if (SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left)
{
MotionControllerData.GripPosition = FVector(-1018.305f, -478.019f, 209.872f);
MotionControllerData.GripRotation = FQuat(-0.116352126f, 0.039430488f, -0.757644236f, 0.641001403f);
@ -572,15 +580,15 @@ void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControlle
{
MotionControllerData.GripPosition = FVector(-1202.619f, -521.077f, 283.076f);
MotionControllerData.GripRotation = FQuat(0.040843058f, 0.116659224f, 0.980030060f, -0.155767411f);
}
}*/
MotionControllerData.DeviceName = TEXT("OpenXR");
HandTrackingData.DeviceName = TEXT("OpenXR");
SkeletalMappingData.SkeletalTransforms.Empty(SkeletalMappingData.SkeletalTransforms.Num());
FTransform ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); i++)
FTransform ParentTrans = FTransform(MotionControllerData.GripUnrealSpaceRotation, MotionControllerData.GripUnrealSpaceLocation, FVector(1.f));
for (int i = 0; i < HandTrackingData.HandKeyLocations.Num(); i++)
{
SkeletalMappingData.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i], MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
SkeletalMappingData.SkeletalTransforms.Add(FTransform(HandTrackingData.HandKeyRotations[i], HandTrackingData.HandKeyLocations[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
SkeletalMappingData.bHasValidData = (SkeletalMappingData.SkeletalTransforms.Num() == EHandKeypointCount);

View file

@ -0,0 +1,340 @@
#include "Serializers/FBPXRSkeletalRepContainerNetSerializer.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/Serialization/PackedVectorNetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
#include "OpenXRHandPoseComponent.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FBPXRSkeletalRepContainer
// -----------------------------------------------------------------------------
struct FBPXRSkeletalRepContainerNetSerializer
{
inline static const FVectorNetQuantize100NetSerializerConfig Quantize100SerializerConfig;
inline static const FNetSerializerConfig* VectorNetQuantizeNetSerializerConfig = &Quantize100SerializerConfig;
inline static const FNetSerializer* VectorNetQuantizeNetSerializer;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FBPXRSkeletalRepContainerNetSerializer::VectorNetQuantizeNetSerializer = &UE_NET_GET_SERIALIZER(FVectorNetQuantize100NetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FBPXRSkeletalRepContainerNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
struct alignas(8) FSkeletalTransformQuantizedData
{
uint64 Position[4]; // We don't need to store double for tracked device positions, but their forwarded serializer uses it
uint16 Rotation[3];
};
struct alignas(8) FBPXRSkeletalRepContainerQuantizedData
{
uint8 TargetHand;
uint8 bAllowDeformingMesh;
uint8 bEnableUE4HandRepSavings;
uint16 ElementCount;
FSkeletalTransformQuantizedData SkeletalTransforms[EHandKeypointCount];
// List of array data, we know the max elements so we can statically set it to that
// Technically should be -6 since we dont use some but im going to future proof it a bit here
};
typedef FBPXRSkeletalRepContainer SourceType;
typedef FBPXRSkeletalRepContainerQuantizedData QuantizedType;
typedef FBPXRSkeletalRepContainerNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Not using delta as this struct is sent on a timer and not changed incrementally
static constexpr uint32 ElementSizeBytes = sizeof(FSkeletalTransformQuantizedData);
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.TargetHand = (uint8)Source.TargetHand;
Target.bAllowDeformingMesh = Source.bAllowDeformingMesh ? 1 : 0;
Target.bEnableUE4HandRepSavings = Source.bEnableUE4HandRepSavings ? 1 : 0;
// Free data if non null
Target.ElementCount = 0;
const uint32 Num = Source.SkeletalTransforms.Num();
Target.ElementCount = static_cast<uint16>(Num);
if (Num > 0)
{
FRotator TargetRot;
FVector TargetLoc;
for (uint16 i = 0; i < Num; ++i)
{
TargetRot = Source.SkeletalTransforms[i].Rotator();
TargetLoc = Source.SkeletalTransforms[i].GetLocation();
Target.SkeletalTransforms[i].Rotation[0] = FRotator::CompressAxisToShort(TargetRot.Pitch);
Target.SkeletalTransforms[i].Rotation[1] = FRotator::CompressAxisToShort(TargetRot.Yaw);
Target.SkeletalTransforms[i].Rotation[2] = FRotator::CompressAxisToShort(TargetRot.Roll);
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&TargetLoc);
MemberArgs.Target = NetSerializerValuePointer(&Target.SkeletalTransforms[i].Position[0]);
Serializer->Quantize(Context, MemberArgs);
}
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.TargetHand = (EVRSkeletalHandIndex)Source.TargetHand;
Target.bAllowDeformingMesh = Source.bAllowDeformingMesh != 0;
Target.bEnableUE4HandRepSavings = Source.bEnableUE4HandRepSavings != 0;
const uint16 Count = Source.ElementCount;
Target.SkeletalTransforms.Reset();
if (Count > 0)
{
Target.SkeletalTransforms.AddUninitialized(Count);
const FSkeletalTransformQuantizedData* Data = Source.SkeletalTransforms;
FRotator TargetRot;
FVector TargetLoc;
for (int i = 0; i < Count; ++i)
{
TargetRot.Pitch = FRotator::DecompressAxisFromShort(Data[i].Rotation[0]);
TargetRot.Yaw = FRotator::DecompressAxisFromShort(Data[i].Rotation[1]);
TargetRot.Roll = FRotator::DecompressAxisFromShort(Data[i].Rotation[2]);
TargetRot.Normalize();
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&TargetLoc);
MemberArgs.Target = NetSerializerValuePointer(&Data[i].Position[0]);
Serializer->Dequantize(Context, MemberArgs);
Target.SkeletalTransforms[i].SetComponents(TargetRot.Quaternion(), TargetLoc, FVector(1.0f));
}
}
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
// write header bytes (small fields) — do as bytes for compactness
Writer->WriteBits(Source.TargetHand, 8);
Writer->WriteBits(Source.bAllowDeformingMesh, 1);
Writer->WriteBits(Source.bEnableUE4HandRepSavings, 1);
int32 BoneCountAdjustment = 6 + (Source.bEnableUE4HandRepSavings != 0 ? 4 : 0);
uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
bool bHasValidData = Source.ElementCount >= TransformCount;
Writer->WriteBits(bHasValidData, 1);
if (bHasValidData)
{
// write element count (16 bits)
uint32 ElemCount = Source.ElementCount;
if (ElemCount < 1)
{
// Write nothing
ElemCount = 0;
}
Writer->WriteBits(ElemCount, 16);
const FSkeletalTransformQuantizedData* Data = Source.SkeletalTransforms;
for (uint16 i = 0; i < ElemCount; ++i)
{
if (Source.bAllowDeformingMesh)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Data[i].Position[0]);
Serializer->Serialize(Context, MemberArgs);
}
Writer->WriteBits(Data[i].Rotation[0], 16);
Writer->WriteBits(Data[i].Rotation[1], 16);
Writer->WriteBits(Data[i].Rotation[2], 16);
}
}
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
// write header bytes (small fields) — do as bytes for compactness
Target.TargetHand = Reader->ReadBits(8);
Target.bAllowDeformingMesh = Reader->ReadBits(1);
Target.bEnableUE4HandRepSavings = Reader->ReadBits(1);
int32 BoneCountAdjustment = 6 + (Target.bEnableUE4HandRepSavings != 0 ? 4 : 0);
uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
bool bHasValidData = Reader->ReadBits(1) != 0;
if (bHasValidData)
{
// write element count (16 bits)
uint32 ElemCount = Reader->ReadBits(16);
Target.ElementCount = static_cast<uint16>(ElemCount);
if (ElemCount > 0)
{
FSkeletalTransformQuantizedData* Data = Target.SkeletalTransforms;
for (uint16 i = 0; i < ElemCount; ++i)
{
if (Target.bAllowDeformingMesh)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Data[i].Position[0]);
Serializer->Deserialize(Context, MemberArgs);
}
Data[i].Rotation[0] = Reader->ReadBits(16);
Data[i].Rotation[1] = Reader->ReadBits(16);
Data[i].Rotation[2] = Reader->ReadBits(16);
}
}
}
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.bAllowDeformingMesh != R.bAllowDeformingMesh) return false;
if (L.bEnableUE4HandRepSavings != R.bEnableUE4HandRepSavings) return false;
if (L.SkeletalTransforms.Num() != R.SkeletalTransforms.Num()) return false;
// Using L num is valid as we already checked if they are the same count
return FPlatformMemory::Memcmp(L.SkeletalTransforms.GetData(), R.SkeletalTransforms.GetData(), sizeof(FTransform) * L.SkeletalTransforms.Num()) == 0;
}
}
static void Apply(FNetSerializationContext& Context, const FNetApplyArgs& Args)
{
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bAllowDeformingMesh = Source.bAllowDeformingMesh;
Target.bEnableUE4HandRepSavings = Source.bEnableUE4HandRepSavings;
Target.TargetHand = Source.TargetHand;
// Copys it over with new allocations
Target.SkeletalTransforms = Source.SkeletalTransforms;
}
static void CloneDynamicState(FNetSerializationContext& Context, const FNetCloneDynamicStateArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<QuantizedType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
// copy small fields
Target.TargetHand = Source.TargetHand;
Target.bAllowDeformingMesh = Source.bAllowDeformingMesh;
Target.bEnableUE4HandRepSavings = Source.bEnableUE4HandRepSavings;
// copy elements
Target.ElementCount = Source.ElementCount;
const uint32 Count = Target.ElementCount;
if (Count > 0)
{
const SIZE_T Bytes = SIZE_T(Count) * ElementSizeBytes;
FMemory::Memcpy(Target.SkeletalTransforms, Source.SkeletalTransforms, Bytes);
}
}
static void FreeDynamicState(FNetSerializationContext& Context, const FNetFreeDynamicStateArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Source);
Target.ElementCount = 0;
}
};
static const FName PropertyNetSerializerRegistry_NAME_BPXRSkeletalRepContainer("BPXRSkeletalRepContainer");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPXRSkeletalRepContainer, FBPXRSkeletalRepContainerNetSerializer);
FBPXRSkeletalRepContainerNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPXRSkeletalRepContainer);
}
void FBPXRSkeletalRepContainerNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPXRSkeletalRepContainer);
}
UE_NET_IMPLEMENT_SERIALIZER(FBPXRSkeletalRepContainerNetSerializer);
}

View file

@ -93,7 +93,7 @@ public:
static void ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms);
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand = false);
static void GetMockUpControllerData(FXRHandTrackingState& HandTrackingData,FXRMotionControllerState& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand = false);
// Get a list of all currently tracked devices and their types, index in the array is their device index
// Returns failed if the openXR query failed (no interaction profile yet or openXR is not running)

View file

@ -4,6 +4,7 @@
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HeadMountedDisplayTypes.h"
#include "Animation/BoneReference.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
@ -97,6 +98,10 @@ public:
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
EVRSkeletalHandIndex TargetHand;
// Used to select the type of pose to retrieve from OpenXR, default is grip but you will want to use Palm when using the palm source for motion controller
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
EXRControllerPoseType PoseType;
// A world scale override that will replace the engines current value and force into the tracked data if non zero
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
float WorldScaleOverride;
@ -144,6 +149,7 @@ public:
bMirrorLeftRight = false;
bEnableUE4HandRepSavings = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
PoseType = EXRControllerPoseType::Grip;
bHasValidData = false;
LastHandGestureIndex = INDEX_NONE;
LastHandGesture = NAME_None;

View file

@ -0,0 +1,16 @@
#pragma once
#include "Iris/Serialization/NetSerializer.h"
#include "FBPXRSkeletalRepContainerNetSerializer.generated.h"
USTRUCT()
struct FBPXRSkeletalRepContainerNetSerializerConfig : public FNetSerializerConfig
{
GENERATED_BODY()
};
namespace UE::Net
{
UE_NET_DECLARE_SERIALIZER(FBPXRSkeletalRepContainerNetSerializer, OPENXREXPANSIONPLUGIN_API);
}

@ -1 +1 @@
Subproject commit 968fe10c9f08859c64ea88768d5216140fc340d9
Subproject commit 57c25ee516342f3a4140ca52eb9025580a4f2fd8

@ -1 +1 @@
Subproject commit d6fbc18c862cc9d1e9c8418a16273a8562078403
Subproject commit 5fe193da6208fbd9626559a66e332c5642496ab3

View file

@ -1,2 +0,0 @@
* text=auto
*.bat eol=crlf

View file

@ -1,10 +0,0 @@
.hg/
binaries/
deriveddatacache/
.vs/
build/
intermediate/
PACKPLUGIN/
saved/
*.orig

View file

@ -1,19 +0,0 @@
Copyright Joshua Statzer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,48 +0,0 @@
UE Forums Thread
https://forums.unrealengine.com/development-discussion/vr-ar-development/89050-vr-openvr-expansion-plugin
Example Template Project
https://github.com/mordentral/VRExpPluginExample
Website:
www.vreue4.com
***
### Use Of This Plugin ###
This Plugin is intended to add additional functionality to Open/SteamVR/(All VR now) in UE4.
### Plugin Website ###
[VREUE4.com](https://vreue4.com)
### How do I install it? ###
https://vreue4.com/documentation?section=installation
**Guides for migrating between different engine versions of the plugin:**
View the patch notes at www.vreue4.com for migration guides as well.
**Option 1:**
Go to www.vreue4.com and downloaded the pre-built binary version for the engine version you are using (not updated with every daily change, only weekly or with large patches).
Install it into your ProjectName/Plugins Directory (Engine level hasn't worked since 4.25 or so when it stopped letting me reference other plugins when compiling for that).
**Option 2 (More up to date - preferred if possible):**
* Clone Or Download Zip and extract this repository to a folder named "VRExpansionPlugin" in your "ProjectName/Plugins" directory, create this directory if it is missing.
* Add the VRExpansionPlugin to your projects PublicDependencyModuleNames in the projects build.cs if you have c++ code included.
* IF you do not have c++ code, use the Add New button in the editor and add a blank c++ class to your project.
* Open up the generated project .SLN file and build the project from the build menu.
You need to have visual studio installed and follow the UE4 setup guide for it: https://docs.unrealengine.com/latest/INT/Programming/Development/VisualStudioSetup/
### How do I use it? ###
### How do I VR? ###
The template project contains use examples of most of the features of the plugin as well as locomotion modes, interaction methods, and basic multiplayer.

Some files were not shown because too many files have changed in this diff Show more