Initial commit

This commit is contained in:
2025-11-30 11:24:13 +01:00
commit cef5f1cd04
848 changed files with 84470 additions and 0 deletions

2
Plugins/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
torchlite/

View File

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

10
Plugins/AdvancedSessions/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,34 @@
{
"FileVersion": 3,
"FriendlyName": "Advanced Sessions",
"Version": 5.7,
"VersionName": "5.7",
"Description": "Adds new blueprint functions to handle more advanced session operations.",
"Category": "Advanced Sessions Plugin",
"CreatedBy": "Joshua Statzer",
"CreatedByURL": "N/A",
"Modules": [
{
"Name": "AdvancedSessions",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
],
"Plugins": [
{
"Name": "OnlineSubsystem",
"Enabled": true
},
{
"Name": "OnlineSubsystemUtils",
"Enabled": true
}
],
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false
}

View File

@@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,20 @@
using UnrealBuildTool;
using System.IO;
public class AdvancedSessions : ModuleRules
{
public AdvancedSessions(ReadOnlyTargetRules Target) : base(Target)
{
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
//bEnforceIWYU = true;
PublicDefinitions.Add("WITH_ADVANCED_SESSIONS=1");
// PrivateIncludePaths.AddRange(new string[] { "AdvancedSessions/Private"/*, "OnlineSubsystemSteam/Private"*/ });
// PublicIncludePaths.AddRange(new string[] { "AdvancedSessions/Public" });
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OnlineSubsystem", "CoreUObject", "OnlineSubsystemUtils", "Networking", "Sockets"/*"Voice", "OnlineSubsystemSteam"*/ });
}
}

View File

@@ -0,0 +1,63 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "Engine/LocalPlayer.h"
#include "OnlineSubsystem.h"
#include "BlueprintDataDefinitions.h"
//#include "OnlineFriendsInterface.h"
//#include "OnlineUserInterface.h"
//#include "OnlineMessageInterface.h"
//#include "OnlinePresenceInterface.h"
//#include "Engine/GameInstance.h"
#include "Interfaces/OnlineSessionInterface.h"
//#include "UObjectIterator.h"
#include "AdvancedExternalUILibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedExternalUILog, Log, All);
UCLASS()
class UAdvancedExternalUILibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* External UI Functions *************//
// Show the UI that handles the Friends list
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void ShowFriendsUI(UObject* WorldContextObject, APlayerController *PlayerController, EBlueprintResultSwitch &Result);
// Show the UI that handles inviting people to your game
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void ShowInviteUI(UObject* WorldContextObject, APlayerController *PlayerController, EBlueprintResultSwitch &Result);
// Show the UI that shows the leaderboard (doesn't work with steam)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void ShowLeaderBoardUI(UObject* WorldContextObject, FString LeaderboardName, EBlueprintResultSwitch &Result);
// Show the UI that shows a web URL
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", AutoCreateRefTerm = "AllowedDomains", WorldContext = "WorldContextObject"))
static void ShowWebURLUI(UObject* WorldContextObject, FString URLToShow, EBlueprintResultSwitch &Result, TArray<FString>& AllowedDomains, bool bEmbedded = false , bool bShowBackground = false, bool bShowCloseButton = false, int32 OffsetX = 0, int32 OffsetY = 0, int32 SizeX = 0, int32 SizeY = 0);
// Show the UI that shows a web URL
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (WorldContext = "WorldContextObject"))
static void CloseWebURLUI(UObject* WorldContextObject);
// Show the UI that shows the profile of a uniquenetid
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void ShowProfileUI(UObject* WorldContextObject, const FBPUniqueNetId PlayerViewingProfile, const FBPUniqueNetId PlayerToViewProfileOf, EBlueprintResultSwitch &Result);
// Show the UI that shows the account upgrade UI (doesn't work with steam)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void ShowAccountUpgradeUI(UObject* WorldContextObject, const FBPUniqueNetId PlayerRequestingAccountUpgradeUI, EBlueprintResultSwitch &Result);
};

View File

@@ -0,0 +1,162 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlineMessageInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Engine/LocalPlayer.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSessionSettings.h"
#include "UObject/UObjectIterator.h"
#include "AdvancedFriendsInterface.h"
#include "AdvancedFriendsGameInstance.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedFriendsInterfaceLog, Log, All);
UCLASS()
class ADVANCEDSESSIONS_API UAdvancedFriendsGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UAdvancedFriendsGameInstance(const FObjectInitializer& ObjectInitializer);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
bool bCallFriendInterfaceEventsOnPlayerControllers;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
bool bCallIdentityInterfaceEventsOnPlayerControllers;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
bool bCallVoiceInterfaceEventsOnPlayerControllers;
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;
//*** Session invite received by local ***//
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);
// After a session invite has been accepted by the local player this event is triggered, call JoinSession on the session result to join it
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnSessionInviteReceived(int32 LocalPlayerNum, FBPUniqueNetId PersonInviting, const FString& AppId, const FBlueprintSessionResult& SessionToJoin);
//*** Session invite accepted by local ***//
FOnSessionUserInviteAcceptedDelegate SessionInviteAcceptedDelegate;
FDelegateHandle SessionInviteAcceptedDelegateHandle;
void OnSessionInviteAcceptedMaster(const bool bWasSuccessful, int32 LocalPlayer, TSharedPtr<const FUniqueNetId> PersonInviting, const FOnlineSessionSearchResult& SessionToJoin);
// After a session invite has been accepted by the local player this event is triggered, call JoinSession on the session result to join it
// This function is currently not hooked up in any of Epics default subsystems, it is here for custom subsystems
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnSessionInviteAccepted(int32 LocalPlayerNum, FBPUniqueNetId PersonInvited, const FBlueprintSessionResult& SessionToJoin);
// After a voice status has changed this event is triggered if the bEnableTalkingStatusDelegate property is true
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedVoice")
void OnPlayerTalkingStateChanged(FBPUniqueNetId PlayerId, bool bIsTalking);
void OnPlayerTalkingStateChangedMaster(TSharedRef<const FUniqueNetId> PlayerId, bool bIsTalking);
FOnPlayerTalkingStateChangedDelegate PlayerTalkingStateChangedDelegate;
FDelegateHandle PlayerTalkingStateChangedDelegateHandle;
// Called when the designated LocalUser has changed login state
UFUNCTION(BlueprintImplementableEvent , Category = "AdvancedIdentity", meta = (DisplayName = "OnPlayerLoginChanged"))
void OnPlayerLoginChanged(int32 PlayerNum);
void OnPlayerLoginChangedMaster(int32 PlayerNum);
FOnLoginChangedDelegate PlayerLoginChangedDelegate;
FDelegateHandle PlayerLoginChangedDelegateHandle;
// Called when the designated LocalUser has changed login status
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedIdentity", meta = (DisplayName = "OnPlayerLoginStatusChanged"))
void OnPlayerLoginStatusChanged(int32 PlayerNum, EBPLoginStatus PreviousStatus, EBPLoginStatus NewStatus, FBPUniqueNetId NewPlayerUniqueNetID);
void OnPlayerLoginStatusChangedMaster(int32 PlayerNum, ELoginStatus::Type PreviousStatus, ELoginStatus::Type NewStatus, const FUniqueNetId & NewPlayerUniqueNetID);
FOnLoginStatusChangedDelegate PlayerLoginStatusChangedDelegate;
FDelegateHandle PlayerLoginStatusChangedDelegateHandle;
//*** Session Invite Received From Friend ***//
// REMOVED BECAUSE IT NEVER GETS CALLED
/*FOnSessionInviteReceivedDelegate SessionInviteReceivedDelegate;
FDelegateHandle SessionInviteReceivedDelegateHandle;
void OnSessionInviteReceivedMaster(const FUniqueNetId &InvitedPlayer, const FUniqueNetId &FriendInviting, const FOnlineSessionSearchResult& Session);
// After a session invite has been sent from a friend this event is triggered, call JoinSession on the session result to join it
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnSessionInviteReceived(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &FriendInviting, const FBlueprintSessionResult &Session);
*/
//*** Friend Invite Accepted ***//
/*FOnInviteAcceptedDelegate FriendInviteAcceptedDelegate;
FDelegateHandle FriendInviteAcceptedDelegateHandle;
void OnFriendInviteAcceptedDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &PlayerInvited);
// After a session invite has been accepted by a friend this event is triggered
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnFriendInviteAccepted(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &PlayerInvited);
*/
//*** Friend Invite Rejected ***//
/*FOnInviteRejectedDelegate SessionInviteRejectedByFriendDelegate;
FDelegateHandle InviteRejectedByFriendDelegateHandle;
void OnFriendInviteRejectedDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &PlayerDeclined);
// After a friend invite has been rejected this event is triggered
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnFriendInviteRejected(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &PlayerDeclined);
*/
//*** Removed By Friend ***//
/*FOnFriendRemovedDelegate RemovedByFriendDelegate;
FDelegateHandle RemovedByFriendDelegateHandle;
void OnRemovedByFriendDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &FriendRemoved);
// After a friend removed the player this event is triggered
UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
void OnRemovedByFriend(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &FriendRemoved);*/
};

View File

@@ -0,0 +1,56 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlineMessageInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSessionSettings.h"
#include "UObject/UObjectIterator.h"
#include "BlueprintDataDefinitions.h"
#include "AdvancedFriendsInterface.generated.h"
UINTERFACE(MinimalAPI)
class UAdvancedFriendsInterface : public UInterface
{
GENERATED_UINTERFACE_BODY()
};
class IAdvancedFriendsInterface
{
GENERATED_IINTERFACE_BODY()
public:
// Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnSessionInviteReceived"))
void OnSessionInviteReceived(FBPUniqueNetId PersonInviting, const FBlueprintSessionResult& SearchResult);
// Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnSessionInviteAccepted"))
void OnSessionInviteAccepted(FBPUniqueNetId PersonInvited, const FBlueprintSessionResult& SearchResult);
// Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerVoiceStateChanged"))
void OnPlayerVoiceStateChanged(FBPUniqueNetId PlayerId, bool bIsTalking);
// Called when the designated LocalUser has changed login state
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerLoginChanged"))
void OnPlayerLoginChanged(int32 PlayerNum);
// Called when the designated LocalUser has changed login state
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerLoginStatusChanged"))
void OnPlayerLoginStatusChanged(EBPLoginStatus PreviousStatus, EBPLoginStatus NewStatus, FBPUniqueNetId PlayerUniqueNetID);
// REMOVED BECAUSE IT WAS NEVER BEING CALLED
// Called when the designated LocalUser has received a session invite, use JoinSession on result to connect
//UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "OnSessionInviteReceived"))
//void OnSessionInviteReceived(const FBPUniqueNetId &FriendInviting, const FBlueprintSessionResult &Session);
};

View File

@@ -0,0 +1,56 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "Engine/LocalPlayer.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlineMessageInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "UObject/UObjectIterator.h"
#include "AdvancedFriendsLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedFriendsLog, Log, All);
UCLASS()
class UAdvancedFriendsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Friend List Functions *************//
// Sends an Invite to the current online session to a list of friends
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList", meta = (ExpandEnumAsExecs = "Result"))
static void SendSessionInviteToFriends(APlayerController *PlayerController, const TArray<FBPUniqueNetId> &Friends, EBlueprintResultSwitch &Result);
// Sends an Invite to the current online session to a friend
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList", meta = (ExpandEnumAsExecs = "Result"))
static void SendSessionInviteToFriend(APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId, EBlueprintResultSwitch &Result);
// Get a friend from the previously read/saved friends list (Must Call GetFriends first for this to return anything)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList")
static void GetFriend(APlayerController *PlayerController, const FBPUniqueNetId FriendUniqueNetId, FBPFriendInfo &Friend);
// Get the previously read/saved friends list (Must Call GetFriends first for this to return anything)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList")
static void GetStoredFriendsList(APlayerController *PlayerController, TArray<FBPFriendInfo> &FriendsList);
// Get the previously read/saved recent players list (Must Call GetRecentPlayers first for this to return anything)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|RecentPlayersList")
static void GetStoredRecentPlayersList(FBPUniqueNetId UniqueNetId, TArray<FBPOnlineRecentPlayer> &PlayersList);
// Check if a UniqueNetId is a friend
UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|FriendsList")
static void IsAFriend(APlayerController *PlayerController, const FBPUniqueNetId UniqueNetId, bool &IsFriend);
};

View File

@@ -0,0 +1,71 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Engine/GameInstance.h"
#include "GameFramework/GameModeBase.h"
#include "GameFramework/GameSession.h"
#include "GameFramework/PlayerState.h"
//#include "UObjectIterator.h"
#include "AdvancedGameSession.generated.h"
/**
A quick wrapper around the game session to add a partial ban implementation. Just bans for the duration of the current session
*/
UCLASS(config = Game, notplaceable)
class AAdvancedGameSession : public AGameSession
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(Transient)
TMap<FUniqueNetIdRepl, FText> BanList;
virtual bool BanPlayer(class APlayerController* BannedPlayer, const FText& BanReason)
{
if (APlayerState* PlayerState = (BannedPlayer != NULL) ? BannedPlayer->PlayerState : NULL)
{
FUniqueNetIdRepl UniqueNetID = PlayerState->GetUniqueId();
bool bWasKicked = KickPlayer(BannedPlayer, BanReason);
if (bWasKicked)
{
BanList.Add(UniqueNetID, BanReason);
}
return bWasKicked;
}
return false;
}
// This should really be handled in the game mode asking game session, but I didn't want to force a custom game session AND game mode
// If done in the game mode, we could check prior to actually spooling up any player information in ApproveLogin
virtual void PostLogin(APlayerController* NewPlayer) override
{
if (APlayerState* PlayerState = (NewPlayer != NULL) ? NewPlayer->PlayerState : NULL)
{
FUniqueNetIdRepl UniqueNetID = PlayerState->GetUniqueId();
if (BanList.Contains(UniqueNetID))
{
KickPlayer(NewPlayer, BanList[UniqueNetID]);
}
}
}
};
AAdvancedGameSession::AAdvancedGameSession(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}

View File

@@ -0,0 +1,81 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Engine/LocalPlayer.h"
#include "UObject/UObjectIterator.h"
#include "AdvancedIdentityLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedIdentityLog, Log, All);
UCLASS()
class UAdvancedIdentityLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Identity Functions *************//
// Get the login status of a local player
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void GetLoginStatus(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetID, EBPLoginStatus & LoginStatus, EBlueprintResultSwitch &Result);
// Get the auth token for a local player
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void GetPlayerAuthToken(UObject* WorldContextObject, APlayerController * PlayerController, FString & AuthToken, EBlueprintResultSwitch &Result);
// Get a players nickname
UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity", meta = (WorldContext = "WorldContextObject"))
static void GetPlayerNickname(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetID, FString & PlayerNickname);
//********* User Account Functions *************//
// Get a users account
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void GetUserAccount(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetId, FBPUserOnlineAccount & AccountInfo, EBlueprintResultSwitch &Result);
// Get all known users accounts
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void GetAllUserAccounts(UObject* WorldContextObject, TArray<FBPUserOnlineAccount> & AccountInfos, EBlueprintResultSwitch &Result);
// Get a user account access token
UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
static void GetUserAccountAccessToken(const FBPUserOnlineAccount & AccountInfo, FString & AccessToken);
// Get a user account Auth attribute (depends on subsystem)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
static void GetUserAccountAuthAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AuthAttribute, EBlueprintResultSwitch &Result);
// Set a user account attribute (depends on subsystem)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
static void SetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, const FString & NewAttributeValue, EBlueprintResultSwitch &Result);
// Get user ID
UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
static void GetUserID(const FBPUserOnlineAccount & AccountInfo, FBPUniqueNetId & UniqueNetID);
// Get user accounts real name if possible
UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
static void GetUserAccountRealName(const FBPUserOnlineAccount & AccountInfo, FString & UserName);
// Get user account display name if possible
UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
static void GetUserAccountDisplayName(const FBPUserOnlineAccount & AccountInfo, FString & DisplayName);
// Get user account attribute (depends on subsystem)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
static void GetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AttributeValue, EBlueprintResultSwitch &Result);
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "Modules/ModuleManager.h"
class AdvancedSessions : public IModuleInterface
{
public:
/** IModuleInterface implementation */
void StartupModule();
void ShutdownModule();
};

View File

@@ -0,0 +1,217 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlineMessageInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "GameFramework/GameModeBase.h"
#include "GameFramework/GameSession.h"
//#include "UObjectIterator.h"
#include "AdvancedSessionsLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedSessionsLog, Log, All);
UCLASS()
class UAdvancedSessionsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Session Admin Functions *************//
// Kick a player from the currently active game session, only available on the server
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions", meta = (WorldContext = "WorldContextObject"))
static bool KickPlayer(UObject* WorldContextObject, APlayerController* PlayerToKick, FText KickReason);
// Ban a player from the currently active game session, only available on the server
// Note that the default gamesession class does not implement an actual ban list and just kicks when this is called
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions", meta = (WorldContext = "WorldContextObject"))
static bool BanPlayer(UObject* WorldContextObject, APlayerController* PlayerToBan, FText BanReason);
//********* Session Search Functions *************//
// Adds or modifies session settings in an existing array depending on if they exist already or not
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
static void AddOrModifyExtraSettings(UPARAM(ref) TArray<FSessionPropertyKeyPair> & SettingsArray, UPARAM(ref) TArray<FSessionPropertyKeyPair> & NewOrChangedSettings, TArray<FSessionPropertyKeyPair> & ModifiedSettingsArray);
// Get an array of the session settings from a session search result
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
static void GetExtraSettings(FBlueprintSessionResult SessionResult, TArray<FSessionPropertyKeyPair> & ExtraSettings);
// Get the current session state
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
static void GetSessionState(UObject* WorldContextObject, EBPOnlineSessionState &SessionState);
// Get the current session settings
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
static void GetSessionSettings(UObject* WorldContextObject, int32 &NumConnections, int32 &NumPrivateConnections, bool &bIsLAN, bool &bIsDedicated, bool &bAllowInvites, bool &bAllowJoinInProgress, bool &bIsAnticheatEnabled, int32 &BuildUniqueID, TArray<FSessionPropertyKeyPair> &ExtraSettings, EBlueprintResultSwitch &Result);
// Check if someone is in the current session
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
static void IsPlayerInSession(UObject* WorldContextObject, const FBPUniqueNetId &PlayerToCheck, bool &bIsInSession);
// Make a literal session search parameter
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionsSearchSetting MakeLiteralSessionSearchProperty(FSessionPropertyKeyPair SessionSearchProperty, EOnlineComparisonOpRedux ComparisonOp);
//********* Session Information Functions ***********//
// Check if a session result is valid or not
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
static bool IsValidSession(const FBlueprintSessionResult & SessionResult);
// Get a string copy of a session ID
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
static void GetSessionID_AsString(const FBlueprintSessionResult & SessionResult, FString& SessionID);
// Get a string copy of the current session ID
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
static void GetCurrentSessionID_AsString(UObject* WorldContextObject, FString& SessionID);
// Get the Unique Current Build ID
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
static void GetCurrentUniqueBuildID(int32 &UniqueBuildId);
// Get the Unique Build ID from a session search result
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
static void GetUniqueBuildID(FBlueprintSessionResult SessionResult, int32 &UniqueBuildId);
// Thanks CriErr for submission
// Get session property Key Name value
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
static FName GetSessionPropertyKey(const FSessionPropertyKeyPair& SessionProperty);
// Find session property by Name
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result"))
static void FindSessionPropertyByName(const TArray<FSessionPropertyKeyPair>& ExtraSettings, FName SettingsName, EBlueprintResultSwitch &Result, FSessionPropertyKeyPair& OutProperty);
// Find session property index by Name
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result"))
static void FindSessionPropertyIndexByName(const TArray<FSessionPropertyKeyPair>& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, int32& OutIndex);
/// Removed the Index_None part of the last function, that isn't accessible in blueprint, better to return success/failure
// End Thanks CriErr :p
// Get session custom information key/value as Byte (For Enums)
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
static void GetSessionPropertyByte(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, uint8 &SettingValue);
// Get session custom information key/value as Bool
// Steam only currently supports Int,Float,String,BYTE values for search filtering!!!
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
static void GetSessionPropertyBool(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, bool &SettingValue);
// Get session custom information key/value as String
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
static void GetSessionPropertyString(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, FString &SettingValue);
// Get session custom information key/value as Int
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
static void GetSessionPropertyInt(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, int32 &SettingValue);
// Get session custom information key/value as Float
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
static void GetSessionPropertyFloat(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, float &SettingValue);
// Make a literal session custom information key/value pair from Byte (For Enums)
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionPropertyKeyPair MakeLiteralSessionPropertyByte(FName Key, uint8 Value);
// Make a literal session custom information key/value pair from Bool
// Steam only currently supports Int,Float,String,BYTE values for search filtering!
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionPropertyKeyPair MakeLiteralSessionPropertyBool(FName Key, bool Value);
// Make a literal session custom information key/value pair from String
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionPropertyKeyPair MakeLiteralSessionPropertyString(FName Key, FString Value);
// Make a literal session custom information key/value pair from Int
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionPropertyKeyPair MakeLiteralSessionPropertyInt(FName Key, int32 Value);
// Make a literal session custom information key/value pair from Float
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
static FSessionPropertyKeyPair MakeLiteralSessionPropertyFloat(FName Key, float Value);
//******* Player ID functions *********//
// Get the unique net id of a network player attached to the given controller
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);
// Return True if Unique Net ID is valid
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static bool IsValidUniqueNetID(const FBPUniqueNetId &UniqueNetId);
/* Returns true if the values are equal (A == B) */
UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal Unique Net ID", CompactNodeTitle = "==", Keywords = "== equal"), Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static bool EqualEqual_UNetIDUnetID(const FBPUniqueNetId &A, const FBPUniqueNetId &B);
/** Converts a FBPUniqueNetID into a FUniqueNetID_Repl */
UFUNCTION(BlueprintPure, meta = (DisplayName = "ToUniqueNetIDRepl (Unique Net ID)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
static FUniqueNetIdRepl Conv_BPUniqueIDToUniqueNetIDRepl(const FBPUniqueNetId& InUniqueID);
// Check if a UniqueNetId is a friend
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|UniqueNetId")
static void UniqueNetIdToString(const FBPUniqueNetId &UniqueNetId, FString &String);
//******** Player Name Functions **********//
// Get the player name of a network player attached to the given controller
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerName")
static void GetPlayerName(APlayerController *PlayerController, FString &PlayerName);
// Set the player name of a network player attached to the given controller
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|PlayerInfo|PlayerName")
static void SetPlayerName(APlayerController *PlayerController, FString PlayerName);
//********** Misc Player Info Functions *********//
// Get the number of network players
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|Misc", meta = (bIgnoreSelf = "true", WorldContext = "WorldContextObject", DisplayName = "GetNumNetworkPlayers"))
static void GetNumberOfNetworkPlayers(UObject* WorldContextObject, int32 &NumNetPlayers);
// Get the network player index of the given controller
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|Misc")
static void GetNetPlayerIndex(APlayerController *PlayerController, int32 &NetPlayerIndex);
// Checks if the stated session subsystem is active
UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|Misc")
static bool HasOnlineSubsystem(FName SubSystemName);
//**** Seamless travel Functions ****//
//Exposes Server travel to blueprint
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Online|AdvancedSessions|Seamless", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool ServerTravel(UObject* WorldContextObject, const FString& InURL, bool bAbsolute, bool bShouldSkipGameNotify);
};

View File

@@ -0,0 +1,99 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "BlueprintDataDefinitions.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/VoiceInterface.h"
//#include "OnlineFriendsInterface.h"
//#include "OnlineUserInterface.h"
//#include "OnlineMessageInterface.h"
//#include "OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
//#include "OnlineSessionInterface.h"
#include "UObject/UObjectIterator.h"
#include "AdvancedVoiceLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedVoiceLog, Log, All);
UCLASS()
class UAdvancedVoiceLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Voice Library Functions *************//
// Get if a headset is present for the specified local user
UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo", meta = (WorldContext = "WorldContextObject"))
static void IsHeadsetPresent(UObject* WorldContextObject, bool & bHasHeadset, uint8 LocalPlayerNum = 0);
// Starts networked voice, allows push to talk in coordination with StopNetworkedVoice
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void StartNetworkedVoice(UObject* WorldContextObject, uint8 LocalPlayerNum = 0);
// Stops networked voice, allows push to talk in coordination with StartNetworkedVoice
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void StopNetworkedVoice(UObject* WorldContextObject, uint8 LocalPlayerNum = 0);
// Registers a local player as someone interested in voice data
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static bool RegisterLocalTalker(UObject* WorldContextObject, uint8 LocalPlayerNum = 0);
// Registers all signed in players as local talkers
// This is already done automatically, only do it manually if you unregistered someone
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void RegisterAllLocalTalkers(UObject* WorldContextObject);
// UnRegisters local player as a local talker
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void UnRegisterLocalTalker(UObject* WorldContextObject, uint8 LocalPlayerNum = 0);
// UnRegisters all signed in players as local talkers
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void UnRegisterAllLocalTalkers(UObject* WorldContextObject);
// Registers a remote player as a talker
// This is already done automatically, only do it manually if you unregistered someone
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static bool RegisterRemoteTalker(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId);
// UnRegisters a remote player as a talker
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static bool UnRegisterRemoteTalker(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId);
// UnRegisters all remote players as talkers
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static void RemoveAllRemoteTalkers(UObject* WorldContextObject);
// Returns whether a local player is currently talking
UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo", meta = (WorldContext = "WorldContextObject"))
static bool IsLocalPlayerTalking(UObject* WorldContextObject, uint8 LocalPlayerNum);
// Returns whether a remote player is currently talking
UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo", meta = (WorldContext = "WorldContextObject"))
static bool IsRemotePlayerTalking(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId);
// Returns whether a player is muted for the specified local player
UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo", meta = (WorldContext = "WorldContextObject"))
static bool IsPlayerMuted(UObject* WorldContextObject, uint8 LocalUserNumChecking, const FBPUniqueNetId& UniqueNetId);
// Mutes the player associated with the uniquenetid for the specified local player, if IsSystemWide is true then it will attempt to mute globally for the player
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static bool MuteRemoteTalker(UObject* WorldContextObject, uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide = false);
// UnMutes the player associated with the uniquenetid for the specified local player, if IsSystemWide is true then it will attempt to unmute globally for the player
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice", meta = (WorldContext = "WorldContextObject"))
static bool UnMuteRemoteTalker(UObject* WorldContextObject, uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide = false);
// Gets the number of local talkers for this system
UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo", meta = (WorldContext = "WorldContextObject"))
static void GetNumLocalTalkers(UObject* WorldContextObject, int32 & NumLocalTalkers);
};

View File

@@ -0,0 +1,55 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Engine/LocalPlayer.h"
#include "AutoLoginUserCallbackProxy.generated.h"
UCLASS(MinimalAPI)
class UAutoLoginUserCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
/**
* Logs the player into the online service using parameters passed on the
* command line. Expects -AUTH_LOGIN=<UserName> -AUTH_PASSWORD=<password>. If either
* are missing, the function returns false and doesn't start the login
* process
*
* @param LocalUserNum the controller number of the associated user
*
*/
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
static UAutoLoginUserCallbackProxy* AutoLoginUser(UObject* WorldContextObject, int32 LocalUserNum);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal);
private:
// The controller number of the associated user
int32 LocalUserNumber;
// The delegate executed by the online subsystem
FOnLoginCompleteDelegate Delegate;
// Handle to the registered OnDestroySessionComplete delegate
FDelegateHandle DelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,435 @@
#pragma once
#include "CoreMinimal.h"
//#include "EngineMinimal.h"
#include "Engine/Engine.h"
#include "GameFramework/PlayerState.h"
//#include "Core.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSessionSettings.h"
#include "OnlineDelegateMacros.h"
#include "OnlineSubsystem.h"
#include "OnlineSubsystemImpl.h"
#include "OnlineSubsystemUtils.h"
#include "OnlineSubsystemUtilsModule.h"
#include "GameFramework/PlayerController.h"
#include "Modules/ModuleManager.h"
#include "OnlineSubsystemUtilsClasses.h"
#include "BlueprintDataDefinitions.generated.h"
UENUM(BlueprintType)
enum class EBPUserPrivileges : uint8
{
/** Whether the user can play at all, online or offline - may be age restricted */
CanPlay,
/** Whether the user can play in online modes */
CanPlayOnline,
/** Whether the user can use voice and text chat */
CanCommunicateOnline,
/** Whether the user can use content generated by other users */
CanUseUserGeneratedContent
};
UENUM(BlueprintType)
enum class EBPLoginStatus : uint8
{
/** Player has not logged in or chosen a local profile */
NotLoggedIn,
/** Player is using a local profile but is not logged in */
UsingLocalProfile,
/** Player has been validated by the platform specific authentication service */
LoggedIn
};
USTRUCT(BlueprintType)
struct FBPUserOnlineAccount
{
GENERATED_USTRUCT_BODY()
public:
TSharedPtr<FUserOnlineAccount> UserAccountInfo;
FBPUserOnlineAccount()
{
}
FBPUserOnlineAccount(TSharedPtr<FUserOnlineAccount> UserAccount)
{
UserAccountInfo = UserAccount;
}
};
UENUM()
enum class ESessionSettingSearchResult : uint8
{
// Found the setting
Found,
// Did not find the setting
NotFound,
// Was not the correct type
WrongType
};
// This makes a lot of the blueprint functions cleaner
UENUM()
enum class EBlueprintResultSwitch : uint8
{
// On Success
OnSuccess,
// On Failure
OnFailure
};
// This makes a lot of the blueprint functions cleaner
UENUM()
enum class EBlueprintAsyncResultSwitch : uint8
{
// On Success
OnSuccess,
// Still loading
AsyncLoading,
// On Failure
OnFailure
};
// This is to define server type searches
UENUM(BlueprintType)
enum class EBPServerPresenceSearchType : uint8
{
AllServers,
ClientServersOnly,
DedicatedServersOnly
};
// Wanted this to be switchable in the editor
UENUM(BlueprintType)
enum class EBPOnlinePresenceState : uint8
{
Online,
Offline,
Away,
ExtendedAway,
DoNotDisturb,
Chat
};
UENUM(BlueprintType)
enum class EBPOnlineSessionState : uint8
{
/** An online session has not been created yet */
NoSession,
/** An online session is in the process of being created */
Creating,
/** Session has been created but the session hasn't started (pre match lobby) */
Pending,
/** Session has been asked to start (may take time due to communication with backend) */
Starting,
/** The current session has started. Sessions with join in progress disabled are no longer joinable */
InProgress,
/** The session is still valid, but the session is no longer being played (post match lobby) */
Ending,
/** The session is closed and any stats committed */
Ended,
/** The session is being destroyed */
Destroying
};
// Boy oh boy is this a dirty hack, but I can't figure out a good way to do it otherwise at the moment
// The UniqueNetId is an abstract class so I can't exactly re-initialize it to make a shared pointer on some functions
// So I made the blueprintable UniqueNetID into a dual variable struct with access functions and I am converting the const var for the pointer
// I really need to re-think this later
USTRUCT(BlueprintType)
struct FBPUniqueNetId
{
GENERATED_USTRUCT_BODY()
private:
bool bUseDirectPointer;
public:
TSharedPtr<const FUniqueNetId> UniqueNetId;
const FUniqueNetId * UniqueNetIdPtr;
void SetUniqueNetId(const TSharedPtr<const FUniqueNetId> &ID)
{
bUseDirectPointer = false;
UniqueNetIdPtr = nullptr;
UniqueNetId = ID;
}
void SetUniqueNetId(const FUniqueNetId *ID)
{
bUseDirectPointer = true;
UniqueNetIdPtr = ID;
}
bool IsValid() const
{
if (bUseDirectPointer && UniqueNetIdPtr != nullptr && UniqueNetIdPtr->IsValid())
{
return true;
}
else if (UniqueNetId.IsValid())
{
return true;
}
else
return false;
}
const FUniqueNetId* GetUniqueNetId() const
{
if (bUseDirectPointer && UniqueNetIdPtr != nullptr)
{
// No longer converting to non const as all functions now pass const UniqueNetIds
return /*const_cast<FUniqueNetId*>*/(UniqueNetIdPtr);
}
else if (UniqueNetId.IsValid())
{
return UniqueNetId.Get();
}
else
return nullptr;
}
// Adding in a compare operator so that std functions will work with this struct
FORCEINLINE bool operator==(const FBPUniqueNetId& Other) const
{
return (IsValid() && Other.IsValid() && (*GetUniqueNetId() == *Other.GetUniqueNetId()));
}
FORCEINLINE bool operator!=(const FBPUniqueNetId& Other) const
{
return !(IsValid() && Other.IsValid() && (*GetUniqueNetId() == *Other.GetUniqueNetId()));
}
FBPUniqueNetId()
{
bUseDirectPointer = false;
UniqueNetIdPtr = nullptr;
}
};
USTRUCT(BluePrintType)
struct FBPOnlineUser
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FBPUniqueNetId UniqueNetId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString RealName;
};
USTRUCT(BluePrintType)
struct FBPOnlineRecentPlayer : public FBPOnlineUser
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString LastSeen;
};
USTRUCT(BlueprintType)
struct FBPFriendPresenceInfo
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bIsOnline = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bIsPlaying = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bIsPlayingThisGame = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bIsJoinable = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bHasVoiceSupport = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
EBPOnlinePresenceState PresenceState = EBPOnlinePresenceState::Offline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString StatusString;
FBPFriendPresenceInfo()
{
bIsOnline = false;
bIsPlaying = false;
bIsPlayingThisGame = false;
bIsJoinable = false;
bHasVoiceSupport = false;
PresenceState = EBPOnlinePresenceState::Offline;
}
};
USTRUCT(BlueprintType)
struct FBPFriendInfo
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FString RealName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
EBPOnlinePresenceState OnlineState = EBPOnlinePresenceState::Offline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FBPUniqueNetId UniqueNetId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
bool bIsPlayingSameGame = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
FBPFriendPresenceInfo PresenceInfo;
FBPFriendInfo()
{
OnlineState = EBPOnlinePresenceState::Offline;
bIsPlayingSameGame = false;
}
};
/** The types of comparison operations for a given search query */
// Used to compare session properties
UENUM(BlueprintType)
enum class EOnlineComparisonOpRedux : uint8
{
Equals,
NotEquals,
GreaterThan,
GreaterThanEquals,
LessThan,
LessThanEquals,
};
// Used to store session properties before converting to FVariantData
USTRUCT(BlueprintType)
struct FSessionPropertyKeyPair
{
GENERATED_USTRUCT_BODY()
FName Key;
FVariantData Data;
};
// Sent to the FindSessionsAdvanced to filter the end results
USTRUCT(BlueprintType)
struct FSessionsSearchSetting
{
GENERATED_USTRUCT_BODY()
//UPROPERTY()
// Had to make a copy of this to account for the original not being exposed to blueprints
/** How is this session setting compared on the backend searches */
EOnlineComparisonOpRedux ComparisonOp;
// The key pair to search for
FSessionPropertyKeyPair PropertyKeyPair;
};
// Couldn't use the default one as it is not exposed to other modules, had to re-create it here
// Helper class for various methods to reduce the call hierarchy
struct FOnlineSubsystemBPCallHelperAdvanced
{
public:
FOnlineSubsystemBPCallHelperAdvanced(const TCHAR* CallFunctionContext, UWorld* World, FName SystemName = NAME_None)
: OnlineSub(Online::GetSubsystem(World, SystemName))
, FunctionContext(CallFunctionContext)
{
if (OnlineSub == nullptr)
{
FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Invalid or uninitialized OnlineSubsystem"), FunctionContext), ELogVerbosity::Warning);
}
}
void QueryIDFromPlayerController(APlayerController* PlayerController)
{
UserID.Reset();
//return const_cast<FUniqueNetId*>(UniqueNetIdPtr);
if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
{
UserID = PlayerState->GetUniqueId().GetUniqueNetId();
if (!UserID.IsValid())
{
FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Cannot map local player to unique net ID"), FunctionContext), ELogVerbosity::Warning);
}
}
else
{
FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Invalid player state"), FunctionContext), ELogVerbosity::Warning);
}
}
bool IsValid() const
{
return UserID.IsValid() && (OnlineSub != nullptr);
}
public:
//TSharedPtr<const FUniqueNetId>& GetUniqueNetId()
TSharedPtr</*class*/ const FUniqueNetId> UserID;
IOnlineSubsystem* const OnlineSub;
const TCHAR* FunctionContext;
};
class FOnlineSearchSettingsEx : public FOnlineSearchSettings
{
/**
* Sets a key value pair combination that defines a search parameter
*
* @param Key key for the setting
* @param Value value of the setting
* @param InType type of comparison
*/
public:
void HardSet(FName Key, const FVariantData& Value, EOnlineComparisonOpRedux CompOp)
{
FOnlineSessionSearchParam* SearchParam = SearchParams.Find(Key);
TEnumAsByte<EOnlineComparisonOp::Type> op;
switch (CompOp)
{
case EOnlineComparisonOpRedux::Equals: op = EOnlineComparisonOp::Equals; break;
case EOnlineComparisonOpRedux::GreaterThan: op = EOnlineComparisonOp::GreaterThan; break;
case EOnlineComparisonOpRedux::GreaterThanEquals: op = EOnlineComparisonOp::GreaterThanEquals; break;
case EOnlineComparisonOpRedux::LessThan: op = EOnlineComparisonOp::LessThan; break;
case EOnlineComparisonOpRedux::LessThanEquals: op = EOnlineComparisonOp::LessThanEquals; break;
case EOnlineComparisonOpRedux::NotEquals: op = EOnlineComparisonOp::NotEquals; break;
default: op = EOnlineComparisonOp::Equals; break;
}
if (SearchParam)
{
SearchParam->Data = Value;
SearchParam->ComparisonOp = op;
}
else
{
FOnlineSessionSearchParam searchSetting((int)0, op);
searchSetting.Data = Value;
SearchParams.Add(Key, searchSetting);
}
}
};
#define INVALID_INDEX -1

View File

@@ -0,0 +1,46 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "BlueprintDataDefinitions.h"
#include "CancelFindSessionsCallbackProxy.generated.h"
UCLASS(MinimalAPI)
class UCancelFindSessionsCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
// Cancels finding sessions
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSessions")
static UCancelFindSessionsCallbackProxy* CancelFindSessions(UObject* WorldContextObject, class APlayerController* PlayerController);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(bool bWasSuccessful);
private:
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed by the online subsystem
FOnCancelFindSessionsCompleteDelegate Delegate;
// Handle to the registered OnDestroySessionComplete delegate
FDelegateHandle DelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,107 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "BlueprintDataDefinitions.h"
#include "CreateSessionCallbackProxyAdvanced.generated.h"
UCLASS(MinimalAPI)
class UCreateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the session was created successfully
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there was an error creating the session
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
/**
* 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 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);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when session creation completes, optionally calls StartSession
void OnCreateCompleted(FName SessionName, bool bWasSuccessful);
// Internal callback when session start completes
void OnStartCompleted(FName SessionName, bool bWasSuccessful);
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed by the online subsystem
FOnCreateSessionCompleteDelegate CreateCompleteDelegate;
// The delegate executed by the online subsystem
FOnStartSessionCompleteDelegate StartCompleteDelegate;
// Handles to the registered delegates above
FDelegateHandle CreateCompleteDelegateHandle;
FDelegateHandle StartCompleteDelegateHandle;
// Number of public connections
int NumPublicConnections;
// Number of private connections
int NumPrivateConnections;
// Whether or not to search LAN
bool bUseLAN;
// Whether or not to allow invites
bool bAllowInvites;
// Whether this is a dedicated server or not
bool bDedicatedServer;
// Whether to use the presence option
bool bUsePresence;
// Whether to prefer the use of lobbies for hosting if the api supports them
bool bUseLobbiesIfAvailable;
// Whether to allow joining via presence
bool bAllowJoinViaPresence;
// Allow joining via presence for friends only
bool bAllowJoinViaPresenceFriendsOnly;
// Delcare the server to be anti cheat protected
bool bAntiCheatProtected;
// Record Stats
bool bUsesStats;
// Should advertise server?
bool bShouldAdvertise;
// Whether to prefer the use of voice chat lobbies if the api supports them
bool bUseLobbiesVoiceChatIfAvailable;
// Whether to start the session automatically after it is created
bool bStartAfterCreate;
// Store extra settings
TArray<FSessionPropertyKeyPair> ExtraSettings;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,49 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "BlueprintDataDefinitions.h"
#include "EndSessionCallbackProxy.generated.h"
UCLASS(MinimalAPI)
class UEndSessionCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
/**
* Ends the current sessions, Generally for almost all uses you should be using the engines native Destroy Session node instead.
* This exists for people using StartSession and optionally hand managing the session state.
*/
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSessions")
static UEndSessionCallbackProxy* EndSession(UObject* WorldContextObject, class APlayerController* PlayerController);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(FName SessionName, bool bWasSuccessful);
private:
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed by the online subsystem
FOnEndSessionCompleteDelegate Delegate;
// Handle to the registered OnDestroySessionComplete delegate
FDelegateHandle DelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,51 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Engine/LocalPlayer.h"
#include "FindFriendSessionCallbackProxy.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(AdvancedFindFriendSessionLog, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintFindFriendSessionDelegate, const TArray<FBlueprintSessionResult> &, SessionInfo);
UCLASS(MinimalAPI)
class UFindFriendSessionCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the friends list successfully was retrieved
UPROPERTY(BlueprintAssignable)
FBlueprintFindFriendSessionDelegate OnSuccess;
// Called when there was an error retrieving the friends list
UPROPERTY(BlueprintAssignable)
FBlueprintFindFriendSessionDelegate OnFailure;
// Attempts to get the current session that a friend is in
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
static UFindFriendSessionCallbackProxy* FindFriendSession(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId);
virtual void Activate() override;
private:
// Internal callback when the friends list is retrieved
void OnFindFriendSessionCompleted(int32 LocalPlayer, bool bWasSuccessful, const TArray<FOnlineSessionSearchResult>& SessionInfo);
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The UniqueNetID of the person to invite
FBPUniqueNetId cUniqueNetId;
// The delegate to call on completion
FOnFindFriendSessionCompleteDelegate OnFindFriendSessionCompleteDelegate;
// Handles to the registered delegates above
FDelegateHandle FindFriendSessionCompleteDelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,109 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "FindSessionsCallbackProxy.h"
#include "BlueprintDataDefinitions.h"
#include "FindSessionsCallbackProxyAdvanced.generated.h"
FORCEINLINE bool operator==(const FBlueprintSessionResult& A, const FBlueprintSessionResult& B)
{
return (A.OnlineResult.IsValid() == B.OnlineResult.IsValid() && (A.OnlineResult.GetSessionIdStr() == B.OnlineResult.GetSessionIdStr()));
}
UCLASS(MinimalAPI)
class UFindSessionsCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful query
UPROPERTY(BlueprintAssignable)
FBlueprintFindSessionsResultDelegate OnSuccess;
// Called when there is an unsuccessful query
UPROPERTY(BlueprintAssignable)
FBlueprintFindSessionsResultDelegate OnFailure;
// 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 bool CompareVariants(const FVariantData &A, const FVariantData &B, EOnlineComparisonOpRedux Comparator);
// Filters an array of session results by the given search parameters, returns a new array with the filtered results
UFUNCTION(BluePrintCallable, meta = (Category = "Online|AdvancedSessions"))
static void FilterSessionResults(const TArray<FBlueprintSessionResult> &SessionResults, const TArray<FSessionsSearchSetting> &Filters, TArray<FBlueprintSessionResult> &FilteredResults);
// Removed, the default built in versions work fine in the normal FindSessionsCallbackProxy
/*UFUNCTION(BlueprintPure, Category = "Online|Session")
static int32 GetPingInMs(const FBlueprintSessionResult& Result);
UFUNCTION(BlueprintPure, Category = "Online|Session")
static FString GetServerName(const FBlueprintSessionResult& Result);
UFUNCTION(BlueprintPure, Category = "Online|Session")
static int32 GetCurrentPlayers(const FBlueprintSessionResult& Result);
UFUNCTION(BlueprintPure, Category = "Online|Session")
static int32 GetMaxPlayers(const FBlueprintSessionResult& Result);*/
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the session search completes, calls out to the public success/failure callbacks
void OnCompleted(bool bSuccess);
bool bRunSecondSearch;
bool bIsOnSecondSearch;
TArray<FBlueprintSessionResult> SessionSearchResults;
private:
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed by the online subsystem
FOnFindSessionsCompleteDelegate Delegate;
// Handle to the registered OnFindSessionsComplete delegate
FDelegateHandle DelegateHandle;
// Object to track search results
TSharedPtr<FOnlineSessionSearch> SearchObject;
TSharedPtr<FOnlineSessionSearch> SearchObjectDedicated;
// Whether or not to search LAN
bool bUseLAN;
// Whether or not to search for dedicated servers
EBPServerPresenceSearchType ServerSearchType;
// Maximum number of results to return
int MaxResults;
// Store extra settings
TArray<FSessionsSearchSetting> SearchSettings;
// Search for empty servers only
bool bEmptyServersOnly;
// Search for non empty servers only
bool bNonEmptyServersOnly;
// Search for secure servers only
bool bSecureServersOnly;
// Search through lobbies
//bool bSearchLobbies;
// Min slots requires to search
int MinSlotsAvailable;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,49 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Engine/LocalPlayer.h"
#include "GetFriendsCallbackProxy.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(AdvancedGetFriendsLog, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGetFriendsListDelegate, const TArray<FBPFriendInfo>&, Results);
UCLASS(MinimalAPI)
class UGetFriendsCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the friends list successfully was retrieved
UPROPERTY(BlueprintAssignable)
FBlueprintGetFriendsListDelegate OnSuccess;
// Called when there was an error retrieving the friends list
UPROPERTY(BlueprintAssignable)
FBlueprintGetFriendsListDelegate OnFailure;
// Gets the players list of friends from the OnlineSubsystem and returns it, can be retrieved later with GetStoredFriendsList
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
static UGetFriendsCallbackProxy* GetAndStoreFriendsList(UObject* WorldContextObject, class APlayerController* PlayerController);
virtual void Activate() override;
private:
// Internal callback when the friends list is retrieved
void OnReadFriendsListCompleted(int32 LocalUserNum, bool bWasSuccessful, const FString& ListName, const FString& ErrorString);
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed
FOnReadFriendsListComplete FriendListReadCompleteDelegate;
// The Type of friends list to get
// Removed because all but the facebook interfaces don't even currently support anything but the default friends list.
//EBPFriendsLists FriendListToGet;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,49 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "GetRecentPlayersCallbackProxy.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(AdvancedGetRecentPlayersLog, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGetRecentPlayersDelegate, const TArray<FBPOnlineRecentPlayer>&, Results);
UCLASS(MinimalAPI)
class UGetRecentPlayersCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the friends list successfully was retrieved
UPROPERTY(BlueprintAssignable)
FBlueprintGetRecentPlayersDelegate OnSuccess;
// Called when there was an error retrieving the friends list
UPROPERTY(BlueprintAssignable)
FBlueprintGetRecentPlayersDelegate OnFailure;
// Gets the list of recent players from the OnlineSubsystem and returns it, can be retrieved later with GetStoredRecentPlayersList, can fail if no recent players are found
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
static UGetRecentPlayersCallbackProxy* GetAndStoreRecentPlayersList(UObject* WorldContextObject, const FBPUniqueNetId &UniqueNetId);
virtual void Activate() override;
private:
// Internal callback when the friends list is retrieved
void OnQueryRecentPlayersCompleted(const FUniqueNetId &UserID, const FString &Namespace, bool bWasSuccessful, const FString& ErrorString);
// Handle to the registered OnFindSessionsComplete delegate
FDelegateHandle DelegateHandle;
// The player controller triggering things
//TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The UniqueNetID of the person to get recent players for
FBPUniqueNetId cUniqueNetId;
// The delegate executed
FOnQueryRecentPlayersCompleteDelegate QueryRecentPlayersCompleteDelegate;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,45 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "GetUserPrivilegeCallbackProxy.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FBlueprintGetUserPrivilegeDelegate,/* const &FBPUniqueNetId, PlayerID,*/ EBPUserPrivileges, QueriedPrivilege, bool, HadPrivilege);
UCLASS(MinimalAPI)
class UGetUserPrivilegeCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FBlueprintGetUserPrivilegeDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
// Gets the privilage of the user
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
static UGetUserPrivilegeCallbackProxy* GetUserPrivilege(UObject* WorldContextObject, const EBPUserPrivileges & PrivilegeToCheck, const FBPUniqueNetId & PlayerUniqueNetID);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(const FUniqueNetId& PlayerID, EUserPrivileges::Type Privilege, uint32 Result);
private:
// The player controller triggering things
FBPUniqueNetId PlayerUniqueNetID;
// Privilege to check
EBPUserPrivileges UserPrivilege;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,55 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Engine/LocalPlayer.h"
#include "LoginUserCallbackProxy.generated.h"
UCLASS(MinimalAPI)
class ULoginUserCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
// Logs into the identity interface
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject", AdvancedDisplay = "AuthType"), Category = "Online|AdvancedIdentity")
static ULoginUserCallbackProxy* LoginUser(UObject* WorldContextObject, class APlayerController* PlayerController, FString UserID, FString UserToken, FString AuthType);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal);
private:
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The user ID
FString UserID;
// The user pass / token
FString UserToken;
FString AuthType;
// The delegate executed by the online subsystem
FOnLoginCompleteDelegate Delegate;
// Handle to the registered OnDestroySessionComplete delegate
FDelegateHandle DelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,47 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Engine/LocalPlayer.h"
#include "LogoutUserCallbackProxy.generated.h"
UCLASS(MinimalAPI)
class ULogoutUserCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an unsuccessful destroy
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
// Logs out of the identity interface
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
static ULogoutUserCallbackProxy* LogoutUser(UObject* WorldContextObject, class APlayerController* PlayerController);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnCompleted(int LocalUserNum, bool bWasSuccessful);
private:
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The delegate executed by the online subsystem
FOnLogoutCompleteDelegate Delegate;
// Handle to the registered OnDestroySessionComplete delegate
FDelegateHandle DelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,27 @@
#pragma once
//#include "EngineMinimal.h"
//#include "Core.h"
//#include "OnlineSessionInterface.h"
//#include "OnlineSessionSettings.h"
//#include "OnlineDelegateMacros.h"
//#include "OnlineSubsystem.h"
//#include "OnlineSubsystemImpl.h"
//#include "OnlineSubsystemUtils.h"
//#include "OnlineSubsystemUtilsModule.h"
//#include "ModuleManager.h"
//#include "OnlineSubsystemUtilsClasses.h"
//#include "BlueprintDataDefinitions.h"
/*#include "VoiceEngineImpl.h"
#include "VoiceInterfaceImpl.h"
#include "Voice.h""
*/
// Found this in the steam controller, seems like a nice thought since steam is throwing errors
// Disable crazy warnings that claim that standard C library is "deprecated".
//#ifdef _MSC_VER
//#pragma warning(push)
//#pragma warning(disable:4996)
//#endif

View File

@@ -0,0 +1,49 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Engine/LocalPlayer.h"
#include "SendFriendInviteCallbackProxy.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(AdvancedSendFriendInviteLog, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBlueprintSendFriendInviteDelegate);
UCLASS(MinimalAPI)
class USendFriendInviteCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the friends list successfully was retrieved
UPROPERTY(BlueprintAssignable)
FBlueprintSendFriendInviteDelegate OnSuccess;
// Called when there was an error retrieving the friends list
UPROPERTY(BlueprintAssignable)
FBlueprintSendFriendInviteDelegate OnFailure;
// Adds a friend who is using the defined UniqueNetId, some interfaces do now allow this function to be called (INCLUDING STEAM)
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
static USendFriendInviteCallbackProxy* SendFriendInvite(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &UniqueNetIDInvited);
virtual void Activate() override;
private:
// Internal callback when the friends list is retrieved
void OnSendInviteComplete(int32 LocalPlayerNum, bool bWasSuccessful, const FUniqueNetId &InvitedPlayer, const FString &ListName, const FString &ErrorString);
// The player controller triggering things
TWeakObjectPtr<APlayerController> PlayerControllerWeakPtr;
// The UniqueNetID of the person to invite
FBPUniqueNetId cUniqueNetId;
// The delegate to call on completion
FOnSendInviteComplete OnSendInviteCompleteDelegate;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,46 @@
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "StartSessionCallbackProxyAdvanced.generated.h"
UCLASS(MinimalAPI)
class UStartSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the session starts successfully
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there is an error starting the session
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
/**
* Starts a session with the default online subsystem. The session needs to be previously created by calling the "CreateAdvancedSession" node.
* @param WorldContextObject
*/
UFUNCTION(
BlueprintCallable
, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject")
, Category = "Online|AdvancedSessions"
)
static UStartSessionCallbackProxyAdvanced* StartAdvancedSession(UObject* WorldContextObject);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when session start completes
void OnStartCompleted(FName SessionName, bool bWasSuccessful);
// The delegate executed by the online subsystem
FOnStartSessionCompleteDelegate StartCompleteDelegate;
// Handles to the registered delegates above
FDelegateHandle StartCompleteDelegateHandle;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,75 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "BlueprintDataDefinitions.h"
#include "UpdateSessionCallbackProxyAdvanced.generated.h"
UCLASS(MinimalAPI)
class UUpdateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when the session was updated successfully
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnSuccess;
// Called when there was an error updating the session
UPROPERTY(BlueprintAssignable)
FEmptyOnlineDelegate OnFailure;
// 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, bool bAllowJoinViaPresence = true, bool bAllowJoinViaPresenceFriendsOnly = false);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
// Internal callback when session creation completes, calls StartSession
void OnUpdateCompleted(FName SessionName, bool bWasSuccessful);
// The delegate executed by the online subsystem
FOnUpdateSessionCompleteDelegate OnUpdateSessionCompleteDelegate;
// Handles to the registered delegates above
FDelegateHandle OnUpdateSessionCompleteDelegateHandle;
// Number of public connections
int NumPublicConnections = 100;
// Number of private connections
int NumPrivateConnections = 0;
// Whether or not to search LAN
bool bUseLAN = false;
// Whether or not to allow invites
bool bAllowInvites = true;
// Store extra settings
TArray<FSessionPropertyKeyPair> ExtraSettings;
// Whether to update the online data
bool bRefreshOnlineData = true;
// 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;
bool bShouldAdvertise = true;
// The world context object in which this call is taking place
TWeakObjectPtr<UObject> WorldContextObject;
};

View File

@@ -0,0 +1,209 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedExternalUILibrary.h"
#include "Engine/LocalPlayer.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedExternalUILog);
void UAdvancedExternalUILibrary::ShowAccountUpgradeUI(UObject* WorldContextObject, const FBPUniqueNetId PlayerRequestingAccountUpgradeUI, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowAccountUpgradeUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ExternalUIInterface->ShowAccountUpgradeUI(*PlayerRequestingAccountUpgradeUI.GetUniqueNetId());
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedExternalUILibrary::ShowProfileUI(UObject* WorldContextObject, const FBPUniqueNetId PlayerViewingProfile, const FBPUniqueNetId PlayerToViewProfileOf, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowProfileUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ExternalUIInterface->ShowProfileUI(*PlayerViewingProfile.GetUniqueNetId(), *PlayerToViewProfileOf.GetUniqueNetId(), NULL);
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedExternalUILibrary::ShowWebURLUI(UObject* WorldContextObject, FString URLToShow, EBlueprintResultSwitch &Result, TArray<FString>& AllowedDomains, bool bEmbedded, bool bShowBackground, bool bShowCloseButton, int32 OffsetX, int32 OffsetY, int32 SizeX, int32 SizeY)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowWebURLUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
URLToShow = URLToShow.Replace(TEXT("http://"), TEXT(""));
URLToShow = URLToShow.Replace(TEXT("https://"), TEXT(""));
FShowWebUrlParams Params;
Params.AllowedDomains = AllowedDomains;
Params.bEmbedded = bEmbedded;
Params.bShowBackground = bShowBackground;
Params.bShowCloseButton = bShowCloseButton;
Params.OffsetX = OffsetX;
Params.OffsetY = OffsetY;
Params.SizeX = SizeX;
Params.SizeY = SizeY;
ExternalUIInterface->ShowWebURL(URLToShow, Params);
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedExternalUILibrary::CloseWebURLUI(UObject* WorldContextObject)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("CloseWebURLUI Failed to get External UI interface!"));
return;
}
ExternalUIInterface->CloseWebURL();
}
void UAdvancedExternalUILibrary::ShowLeaderBoardUI(UObject* WorldContextObject, FString LeaderboardName, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowLeaderboardsUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ExternalUIInterface->ShowLeaderboardUI(LeaderboardName);
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedExternalUILibrary::ShowInviteUI(UObject* WorldContextObject, APlayerController *PlayerController, EBlueprintResultSwitch &Result)
{
if (!PlayerController)
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Had a bad Player Controller!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Failed to get ULocalPlayer for the given PlayerController!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ExternalUIInterface->ShowInviteUI(Player->GetControllerId(), NAME_GameSession);
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedExternalUILibrary::ShowFriendsUI(UObject* WorldContextObject, APlayerController *PlayerController, EBlueprintResultSwitch &Result)
{
if (!PlayerController)
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Had a bad Player Controller!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface(World);
if (!ExternalUIInterface.IsValid())
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Failed to get External UI interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Failed to get ULocalPlayer for the given PlayerController!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ExternalUIInterface->ShowFriendsUI(Player->GetControllerId());
Result = EBlueprintResultSwitch::OnSuccess;
}

View File

@@ -0,0 +1,407 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedFriendsGameInstance.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedFriendsInterfaceLog);
UAdvancedFriendsGameInstance::UAdvancedFriendsGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bCallFriendInterfaceEventsOnPlayerControllers(true)
, bCallIdentityInterfaceEventsOnPlayerControllers(true)
, bCallVoiceInterfaceEventsOnPlayerControllers(true)
, bEnableTalkingStatusDelegate(true)
, SessionInviteReceivedDelegate(FOnSessionInviteReceivedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteReceivedMaster))
, SessionInviteAcceptedDelegate(FOnSessionUserInviteAcceptedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteAcceptedMaster))
, PlayerTalkingStateChangedDelegate(FOnPlayerTalkingStateChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerTalkingStateChangedMaster))
, PlayerLoginChangedDelegate(FOnLoginChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerLoginChangedMaster))
, PlayerLoginStatusChangedDelegate(FOnLoginStatusChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerLoginStatusChangedMaster))
{
}
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());
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsGameInstance Failed to get session system!"));
//return;
}
else
{
// Clear all of the delegate handles here
SessionInterface->ClearOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegateHandle);
SessionInterface->ClearOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegateHandle);
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);
}
if (bEnableTalkingStatusDelegate)
{
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(GetWorld());
if (VoiceInterface.IsValid())
{
VoiceInterface->ClearOnPlayerTalkingStateChangedDelegate_Handle(PlayerTalkingStateChangedDelegateHandle);
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get voice interface!"));
}
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(GetWorld());
if (IdentityInterface.IsValid())
{
IdentityInterface->ClearOnLoginChangedDelegate_Handle(PlayerLoginChangedDelegateHandle);
// I am just defaulting to player 1
IdentityInterface->ClearOnLoginStatusChangedDelegate_Handle(0, PlayerLoginStatusChangedDelegateHandle);
}
Super::Shutdown();
}
void UAdvancedFriendsGameInstance::Init()
{
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());//OnlineSub->GetSessionInterface();
if (SessionInterface.IsValid())
{
// Currently doesn't store a handle or assign a delegate to any local player beyond the first.....should handle?
// Thought about directly handling it but friends for multiple players probably isn't required
// Iterating through the local player TArray only works if it has had players assigned to it, most of the online interfaces don't support
// Multiple logins either (IE: Steam)
SessionInviteAcceptedDelegateHandle = SessionInterface->AddOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegate);
SessionInviteReceivedDelegateHandle = SessionInterface->AddOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegate);
// Custom steam join game delegate
SessionInterface->OnSessionUserInviteAcceptedDelegates.AddUObject(this, &UAdvancedFriendsGameInstance::OnSessionUserInviteAccepted);
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get session interface!"));
//return;
}
// Beginning work on the voice interface
if (bEnableTalkingStatusDelegate)
{
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(GetWorld());
if (VoiceInterface.IsValid())
{
PlayerTalkingStateChangedDelegateHandle = VoiceInterface->AddOnPlayerTalkingStateChangedDelegate_Handle(PlayerTalkingStateChangedDelegate);
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get voice interface!"));
}
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(GetWorld());
if (IdentityInterface.IsValid())
{
PlayerLoginChangedDelegateHandle = IdentityInterface->AddOnLoginChangedDelegate_Handle(PlayerLoginChangedDelegate);
// Just defaulting to player 1
PlayerLoginStatusChangedDelegateHandle = IdentityInterface->AddOnLoginStatusChangedDelegate_Handle(0, PlayerLoginStatusChangedDelegate);
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get identity interface!"));
}
Super::Init();
}
/*void UAdvancedFriendsGameInstance::PostLoad()
{
Super::PostLoad();
}*/
// Removed because it never gets called by the online subsystems
/*void UAdvancedFriendsGameInstance::OnSessionInviteReceivedMaster(const FUniqueNetId &InvitedPlayer, const FUniqueNetId &FriendInviting, const FOnlineSessionSearchResult& Session)
{
// Just call the blueprint event to let the user handle this
FBPUniqueNetId IP, FI;
IP.SetUniqueNetId(&InvitedPlayer);
FI.SetUniqueNetId(&FriendInviting);
FBlueprintSessionResult BPS;
BPS.OnlineResult = Session;
OnSessionInviteReceived(IP,FI,BPS);
TArray<class APlayerState*>& PlayerArray = GetWorld()->GetGameState()->PlayerArray;
const TArray<class ULocalPlayer*>&ControllerArray = this->GetLocalPlayers();
for (int i = 0; i < ControllerArray.Num(); i++)
{
if (*PlayerArray[ControllerArray[i]->PlayerController->NetPlayerIndex]->UniqueId.GetUniqueNetId().Get() == InvitedPlayer)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (ControllerArray[i]->PlayerController->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnSessionInviteReceived(ControllerArray[i]->PlayerController, FI, BPS);
}
break;
}
}
}*/
void UAdvancedFriendsGameInstance::OnPlayerLoginStatusChangedMaster(int32 PlayerNum, ELoginStatus::Type PreviousStatus, ELoginStatus::Type NewStatus, const FUniqueNetId & NewPlayerUniqueNetID)
{
EBPLoginStatus OrigStatus = (EBPLoginStatus)PreviousStatus;
EBPLoginStatus CurrentStatus = (EBPLoginStatus)NewStatus;
FBPUniqueNetId PlayerID;
PlayerID.SetUniqueNetId(&NewPlayerUniqueNetID);
OnPlayerLoginStatusChanged(PlayerNum, OrigStatus,CurrentStatus,PlayerID);
if (bCallIdentityInterfaceEventsOnPlayerControllers)
{
APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), PlayerNum);
if (Player != NULL)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnPlayerLoginStatusChanged(Player, OrigStatus, CurrentStatus, PlayerID);
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnPlayerLoginStatusChangedMaster!"));
}
}
}
void UAdvancedFriendsGameInstance::OnPlayerLoginChangedMaster(int32 PlayerNum)
{
OnPlayerLoginChanged(PlayerNum);
if (bCallIdentityInterfaceEventsOnPlayerControllers)
{
APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), PlayerNum);
if (Player != NULL)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnPlayerLoginChanged(Player, PlayerNum);
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnPlayerLoginChanged!"));
}
}
}
void UAdvancedFriendsGameInstance::OnPlayerTalkingStateChangedMaster(TSharedRef<const FUniqueNetId> PlayerId, bool bIsTalking)
{
FBPUniqueNetId PlayerTalking;
PlayerTalking.SetUniqueNetId(PlayerId);
OnPlayerTalkingStateChanged(PlayerTalking, bIsTalking);
if (bCallVoiceInterfaceEventsOnPlayerControllers)
{
APlayerController* Player = NULL;
for (const ULocalPlayer* LPlayer : LocalPlayers)
{
Player = UGameplayStatics::GetPlayerController(GetWorld(), LPlayer->GetControllerId());
if (Player != NULL)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnPlayerVoiceStateChanged(Player, PlayerTalking, bIsTalking);
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnVoiceStateChanged!"));
}
}
}
}
void UAdvancedFriendsGameInstance::OnSessionInviteReceivedMaster(const FUniqueNetId & PersonInvited, const FUniqueNetId & PersonInviting, const FString& AppId, const FOnlineSessionSearchResult& SessionToJoin)
{
if (SessionToJoin.IsValid())
{
FBlueprintSessionResult BluePrintResult;
BluePrintResult.OnlineResult = SessionToJoin;
FBPUniqueNetId PInvited;
PInvited.SetUniqueNetId(&PersonInvited);
FBPUniqueNetId PInviting;
PInviting.SetUniqueNetId(&PersonInviting);
TArray<APlayerController*> PlayerList;
GEngine->GetAllLocalPlayerControllers(PlayerList);
APlayerController* Player = NULL;
int32 LocalPlayer = 0;
for (int i = 0; i < PlayerList.Num(); i++)
{
if (*PlayerList[i]->PlayerState->GetUniqueId().GetUniqueNetId() == PersonInvited)
{
LocalPlayer = i;
Player = PlayerList[i];
break;
}
}
// 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;
if (Player != NULL)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnSessionInviteReceived(Player, PInviting, BluePrintResult);
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnSessionInviteReceived!"));
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Return a bad search result in OnSessionInviteReceived!"));
}
}
void UAdvancedFriendsGameInstance::OnSessionInviteAcceptedMaster(const bool bWasSuccessful, int32 LocalPlayer, TSharedPtr<const FUniqueNetId> PersonInvited, const FOnlineSessionSearchResult& SessionToJoin)
{
if (bWasSuccessful)
{
if (SessionToJoin.IsValid())
{
FBlueprintSessionResult BluePrintResult;
BluePrintResult.OnlineResult = SessionToJoin;
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);
//IAdvancedFriendsInterface* TheInterface = NULL;
if (Player != NULL)
{
//Run the Event specific to the actor, if the actor has the interface, otherwise ignore
if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
{
IAdvancedFriendsInterface::Execute_OnSessionInviteAccepted(Player,PInvited, BluePrintResult);
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnSessionInviteAccepted!"));
}
}
else
{
UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Return a bad search result in OnSessionInviteAccepted!"));
}
}
}

View File

@@ -0,0 +1,9 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedFriendsInterface.h"
UAdvancedFriendsInterface::UAdvancedFriendsInterface(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}

View File

@@ -0,0 +1,284 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedFriendsLibrary.h"
// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
//General Log
DEFINE_LOG_CATEGORY(AdvancedFriendsLog);
void UAdvancedFriendsLibrary::SendSessionInviteToFriends(APlayerController *PlayerController, const TArray<FBPUniqueNetId> &Friends, EBlueprintResultSwitch &Result)
{
if (!PlayerController)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad Player Controller!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (Friends.Num() < 1)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had no friends in invitation array!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface();
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Failed to get session interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend failed to get LocalPlayer!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
TArray<TSharedRef<const FUniqueNetId>> List;
for (int i = 0; i < Friends.Num(); i++)
{
TSharedRef<const FUniqueNetId> val(Friends[i].UniqueNetId.ToSharedRef());
//TSharedRef<const FUniqueNetId> val(Friends[i].GetUniqueNetId());
List.Add(val);
}
if (SessionInterface->SendSessionInviteToFriends(Player->GetControllerId(), NAME_GameSession, List))
{
Result = EBlueprintResultSwitch::OnSuccess;
return;
}
Result = EBlueprintResultSwitch::OnFailure;
return;
}
void UAdvancedFriendsLibrary::SendSessionInviteToFriend(APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId, EBlueprintResultSwitch &Result)
{
if (!PlayerController)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad Player Controller!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (!FriendUniqueNetId.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad UniqueNetId!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface();
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Failed to get session interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend failed to get LocalPlayer!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (SessionInterface->SendSessionInviteToFriend(Player->GetControllerId(), NAME_GameSession, *FriendUniqueNetId.GetUniqueNetId()))
{
Result = EBlueprintResultSwitch::OnSuccess;
return;
}
Result = EBlueprintResultSwitch::OnFailure;
return;
}
void UAdvancedFriendsLibrary::GetFriend(APlayerController *PlayerController, const FBPUniqueNetId FriendUniqueNetId, FBPFriendInfo &Friend)
{
if (!PlayerController)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Had a bad Player Controller!"));
return;
}
if (!FriendUniqueNetId.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Had a bad UniqueNetId!"));
return;
}
IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
if (!FriendsInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Failed to get friends interface!"));
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend failed to get LocalPlayer!"));
return;
}
TSharedPtr<FOnlineFriend> fr = FriendsInterface->GetFriend(Player->GetControllerId(), *FriendUniqueNetId.GetUniqueNetId(), EFriendsLists::ToString(EFriendsLists::Default));
if (fr.IsValid())
{
const FOnlineUserPresence& pres = fr->GetPresence();
Friend.DisplayName = fr->GetDisplayName();
Friend.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
Friend.RealName = fr->GetRealName();
Friend.UniqueNetId.SetUniqueNetId(fr->GetUserId());
Friend.bIsPlayingSameGame = pres.bIsPlayingThisGame;
Friend.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
Friend.PresenceInfo.bIsJoinable = pres.bIsJoinable;
Friend.PresenceInfo.bIsOnline = pres.bIsOnline;
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;
}
}
void UAdvancedFriendsLibrary::IsAFriend(APlayerController *PlayerController, const FBPUniqueNetId UniqueNetId, bool &IsFriend)
{
if (!PlayerController)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Had a bad Player Controller!"));
return;
}
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
return;
}
IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
if (!FriendsInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Failed to get friends interface!"));
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Failed to get LocalPlayer!"));
return;
}
IsFriend = FriendsInterface->IsFriend(Player->GetControllerId(), *UniqueNetId.GetUniqueNetId(), EFriendsLists::ToString(EFriendsLists::Default));
}
void UAdvancedFriendsLibrary::GetStoredRecentPlayersList(FBPUniqueNetId UniqueNetId, TArray<FBPOnlineRecentPlayer> &PlayersList)
{
IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
if (!FriendsInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetRecentPlayersList Failed to get friends interface!"));
return;
}
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetRecentPlayersList Failed was given an invalid UniqueNetId!"));
return;
}
TArray< TSharedRef<FOnlineRecentPlayer> > PlayerList;
// For now getting all namespaces
FriendsInterface->GetRecentPlayers(*(UniqueNetId.GetUniqueNetId()),"", PlayerList);
for (int32 i = 0; i < PlayerList.Num(); i++)
{
TSharedRef<FOnlineRecentPlayer> Player = PlayerList[i];
FBPOnlineRecentPlayer BPF;
BPF.DisplayName = Player->GetDisplayName();
BPF.RealName = Player->GetRealName();
BPF.UniqueNetId.SetUniqueNetId(Player->GetUserId());
PlayersList.Add(BPF);
}
}
void UAdvancedFriendsLibrary::GetStoredFriendsList(APlayerController *PlayerController, TArray<FBPFriendInfo> &FriendsList)
{
if (!PlayerController)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Had a bad Player Controller!"));
return;
}
IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
if (!FriendsInterface.IsValid())
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Failed to get friends interface!"));
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Failed to get LocalPlayer!"));
return;
}
TArray< TSharedRef<FOnlineFriend> > FriendList;
if (FriendsInterface->GetFriendsList(Player->GetControllerId(), EFriendsLists::ToString((EFriendsLists::Default)), FriendList))
{
for (int32 i = 0; i < FriendList.Num(); i++)
{
FBPFriendInfo BPF;
const FOnlineUserPresence& pres = FriendList[i]->GetPresence();
BPF.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
BPF.DisplayName = FriendList[i]->GetDisplayName();
BPF.RealName = FriendList[i]->GetRealName();
BPF.UniqueNetId.SetUniqueNetId(FriendList[i]->GetUserId());
BPF.bIsPlayingSameGame = pres.bIsPlayingThisGame;
BPF.PresenceInfo.bIsOnline = pres.bIsOnline;
BPF.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
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;
FriendsList.Add(BPF);
}
return;
}
UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Failed to get any friends!"));
return;
}

View File

@@ -0,0 +1,271 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedIdentityLibrary.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedIdentityLog);
void UAdvancedIdentityLibrary::GetPlayerAuthToken(UObject* WorldContextObject, APlayerController * PlayerController, FString & AuthToken, EBlueprintResultSwitch &Result)
{
if (!PlayerController)
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken was passed a bad player controller!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerController->Player);
if (!Player)
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken failed to get LocalPlayer!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(World);
if (!IdentityInterface.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken Failed to get identity interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
AuthToken = IdentityInterface->GetAuthToken(Player->GetControllerId());
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::GetPlayerNickname(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetID, FString & PlayerNickname)
{
if (!UniqueNetID.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerNickname was passed a bad player uniquenetid!"));
return;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(World);
if (!IdentityInterface.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerNickname Failed to get identity interface!"));
return;
}
PlayerNickname = IdentityInterface->GetPlayerNickname(*UniqueNetID.GetUniqueNetId());
}
void UAdvancedIdentityLibrary::GetLoginStatus(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetID, EBPLoginStatus & LoginStatus, EBlueprintResultSwitch &Result)
{
if (!UniqueNetID.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetLoginStatus was passed a bad player uniquenetid!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(World);
if (!IdentityInterface.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetLoginStatus Failed to get identity interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
LoginStatus = (EBPLoginStatus)IdentityInterface->GetLoginStatus(*UniqueNetID.GetUniqueNetId());
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::GetAllUserAccounts(UObject* WorldContextObject, TArray<FBPUserOnlineAccount> & AccountInfos, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(World);
if (!IdentityInterface.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetAllUserAccounts Failed to get identity interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
TArray<TSharedPtr<FUserOnlineAccount>> accountInfos = IdentityInterface->GetAllUserAccounts();
for (int i = 0; i < accountInfos.Num(); ++i)
{
AccountInfos.Add(FBPUserOnlineAccount(accountInfos[i]));
}
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::GetUserAccount(UObject* WorldContextObject, const FBPUniqueNetId & UniqueNetId, FBPUserOnlineAccount & AccountInfo, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
Result = EBlueprintResultSwitch::OnFailure;
return;
}
IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(World);
if(!UniqueNetId.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount was passed a bad unique net id!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (!IdentityInterface.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount Failed to get identity interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
TSharedPtr<FUserOnlineAccount> accountInfo = IdentityInterface->GetUserAccount(*UniqueNetId.GetUniqueNetId());
if (!accountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount Failed to get the account!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
AccountInfo.UserAccountInfo = accountInfo;
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::GetUserAccountAccessToken(const FBPUserOnlineAccount & AccountInfo, FString & AccessToken)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAccessToken was passed an invalid account!"));
return;
}
AccessToken = AccountInfo.UserAccountInfo->GetAccessToken();
}
void UAdvancedIdentityLibrary::GetUserAccountAuthAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AuthAttribute, EBlueprintResultSwitch &Result)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAuthAttribute was passed an invalid account!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (!AccountInfo.UserAccountInfo->GetAuthAttribute(AttributeName, AuthAttribute))
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAuthAttribute couldn't find the attribute!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::SetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, const FString & NewAttributeValue, EBlueprintResultSwitch &Result)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("SetUserAccountAuthAttribute was passed an invalid account!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (!AccountInfo.UserAccountInfo->SetUserAttribute(AttributeName, NewAttributeValue))
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("SetUserAccountAuthAttribute was unable to set the attribute!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedIdentityLibrary::GetUserID(const FBPUserOnlineAccount & AccountInfo, FBPUniqueNetId & UniqueNetID)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserID was passed an invalid account!"));
return;
}
UniqueNetID.SetUniqueNetId(AccountInfo.UserAccountInfo->GetUserId());
}
void UAdvancedIdentityLibrary::GetUserAccountRealName(const FBPUserOnlineAccount & AccountInfo, FString & UserName)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountRealName was passed an invalid account!"));
return;
}
UserName = AccountInfo.UserAccountInfo->GetRealName();
}
void UAdvancedIdentityLibrary::GetUserAccountDisplayName(const FBPUserOnlineAccount & AccountInfo, FString & DisplayName)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountDisplayName was passed an invalid account!"));
return;
}
DisplayName = AccountInfo.UserAccountInfo->GetDisplayName();
}
void UAdvancedIdentityLibrary::GetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AttributeValue, EBlueprintResultSwitch &Result)
{
if (!AccountInfo.UserAccountInfo.IsValid())
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAttribute was passed an invalid account!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (!AccountInfo.UserAccountInfo->GetUserAttribute(AttributeName, AttributeValue))
{
UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAttribute failed to get user attribute!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
Result = EBlueprintResultSwitch::OnSuccess;
}

View File

@@ -0,0 +1,12 @@
//#include "StandAlonePrivatePCH.h"
#include "AdvancedSessions.h"
void AdvancedSessions::StartupModule()
{
}
void AdvancedSessions::ShutdownModule()
{
}
IMPLEMENT_MODULE(AdvancedSessions, AdvancedSessions)

View File

@@ -0,0 +1,563 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedSessionsLibrary.h"
#include "GameFramework/PlayerState.h"
#include "GameFramework/GameStateBase.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedSessionsLog);
bool UAdvancedSessionsLibrary::KickPlayer(UObject* WorldContextObject, APlayerController* PlayerToKick, FText KickReason)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World)
{
if (AGameModeBase* GameMode = World->GetAuthGameMode())
{
if (GameMode->GameSession)
{
return GameMode->GameSession->KickPlayer(PlayerToKick, KickReason);
}
}
}
return false;
}
bool UAdvancedSessionsLibrary::BanPlayer(UObject* WorldContextObject, APlayerController* PlayerToBan, FText BanReason)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World)
{
if (AGameModeBase* GameMode = World->GetAuthGameMode())
{
if (GameMode->GameSession)
{
return GameMode->GameSession->BanPlayer(PlayerToBan, BanReason);
}
}
}
return false;
}
bool UAdvancedSessionsLibrary::IsValidSession(const FBlueprintSessionResult & SessionResult)
{
return SessionResult.OnlineResult.IsValid();
}
void UAdvancedSessionsLibrary::GetSessionID_AsString(const FBlueprintSessionResult & SessionResult, FString& SessionID)
{
const TSharedPtr<class FOnlineSessionInfo> SessionInfo = SessionResult.OnlineResult.Session.SessionInfo;
if (SessionInfo.IsValid() && SessionInfo->IsValid() && SessionInfo->GetSessionId().IsValid())
{
SessionID = SessionInfo->GetSessionId().ToString();
return;
}
// Zero the string out if we didn't have a valid one, in case this is called in c++
SessionID.Empty();
}
void UAdvancedSessionsLibrary::GetCurrentSessionID_AsString(UObject* WorldContextObject, FString& SessionID)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetCurrentSessionID_AsString couldn't get the session interface!"));
SessionID.Empty();
return;
}
const FNamedOnlineSession* Session = SessionInterface->GetNamedSession(NAME_GameSession);
if (Session != nullptr)
{
const TSharedPtr<class FOnlineSessionInfo> SessionInfo = Session->SessionInfo;
if (SessionInfo.IsValid() && SessionInfo->IsValid() && SessionInfo->GetSessionId().IsValid())
{
SessionID = SessionInfo->GetSessionId().ToString();
return;
}
}
// Zero the string out if we didn't have a valid one, in case this is called in c++
SessionID.Empty();
}
void UAdvancedSessionsLibrary::GetCurrentUniqueBuildID(int32 &UniqueBuildId)
{
UniqueBuildId = GetBuildUniqueId();
}
void UAdvancedSessionsLibrary::GetUniqueBuildID(FBlueprintSessionResult SessionResult, int32 &UniqueBuildId)
{
UniqueBuildId = SessionResult.OnlineResult.Session.SessionSettings.BuildUniqueId;
}
FName UAdvancedSessionsLibrary::GetSessionPropertyKey(const FSessionPropertyKeyPair& SessionProperty)
{
return SessionProperty.Key;
}
void UAdvancedSessionsLibrary::FindSessionPropertyByName(const TArray<FSessionPropertyKeyPair>& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, FSessionPropertyKeyPair& OutProperty)
{
const FSessionPropertyKeyPair* prop = ExtraSettings.FindByPredicate([&](const FSessionPropertyKeyPair& it) {return it.Key == SettingName; });
if (prop)
{
Result = EBlueprintResultSwitch::OnSuccess;
OutProperty = *prop;
return;
}
Result = EBlueprintResultSwitch::OnFailure;
}
void UAdvancedSessionsLibrary::FindSessionPropertyIndexByName(const TArray<FSessionPropertyKeyPair>& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, int32& OutIndex)
{
OutIndex = ExtraSettings.IndexOfByPredicate([&](const FSessionPropertyKeyPair& it) {return it.Key == SettingName; });
Result = OutIndex != INDEX_NONE ? EBlueprintResultSwitch::OnSuccess : EBlueprintResultSwitch::OnFailure;
}
void UAdvancedSessionsLibrary::AddOrModifyExtraSettings(UPARAM(ref) TArray<FSessionPropertyKeyPair> & SettingsArray, UPARAM(ref) TArray<FSessionPropertyKeyPair> & NewOrChangedSettings, TArray<FSessionPropertyKeyPair> & ModifiedSettingsArray)
{
ModifiedSettingsArray = SettingsArray;
bool bFoundSetting = false;
// For each new setting
for (const FSessionPropertyKeyPair& Setting : NewOrChangedSettings)
{
bFoundSetting = false;
for (FSessionPropertyKeyPair & itr : ModifiedSettingsArray)
{
// Manually comparing the keys
if (itr.Key == Setting.Key)
{
bFoundSetting = true;
itr.Data = Setting.Data;
}
}
// If it was not found, add to the array instead
if (!bFoundSetting)
{
ModifiedSettingsArray.Add(Setting);
}
}
}
void UAdvancedSessionsLibrary::GetExtraSettings(FBlueprintSessionResult SessionResult, TArray<FSessionPropertyKeyPair> & ExtraSettings)
{
FSessionPropertyKeyPair NewSetting;
for (auto& Elem : SessionResult.OnlineResult.Session.SessionSettings.Settings)
{
NewSetting.Key = Elem.Key;
NewSetting.Data = Elem.Value.Data;
ExtraSettings.Add(NewSetting);
}
}
void UAdvancedSessionsLibrary::GetSessionState(UObject* WorldContextObject, EBPOnlineSessionState &SessionState)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionState couldn't get the session interface!"));
return;
}
SessionState = ((EBPOnlineSessionState)SessionInterface->GetSessionState(NAME_GameSession));
}
void UAdvancedSessionsLibrary::GetSessionSettings(UObject* WorldContextObject, int32 &NumConnections, int32 &NumPrivateConnections, bool &bIsLAN, bool &bIsDedicated, bool &bAllowInvites, bool &bAllowJoinInProgress, bool &bIsAnticheatEnabled, int32 &BuildUniqueID, TArray<FSessionPropertyKeyPair> &ExtraSettings, EBlueprintResultSwitch &Result)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionSettings couldn't get the session interface!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
FOnlineSessionSettings* settings = SessionInterface->GetSessionSettings(NAME_GameSession);
if (!settings)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionSettings couldn't get the session settings!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
BuildUniqueID = settings->BuildUniqueId;
NumConnections = settings->NumPublicConnections;
NumPrivateConnections = settings->NumPrivateConnections;
bIsLAN = settings->bIsLANMatch;
bIsDedicated = settings->bIsDedicated;
bIsAnticheatEnabled = settings->bAntiCheatProtected;
bAllowInvites = settings->bAllowInvites;
bAllowJoinInProgress = settings->bAllowJoinInProgress;
FSessionPropertyKeyPair NewSetting;
for (auto& Elem : settings->Settings)
{
NewSetting.Key = Elem.Key;
NewSetting.Data = Elem.Value.Data;
ExtraSettings.Add(NewSetting);
}
Result = EBlueprintResultSwitch::OnSuccess;
}
void UAdvancedSessionsLibrary::IsPlayerInSession(UObject* WorldContextObject, const FBPUniqueNetId &PlayerToCheck, bool &bIsInSession)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
if (!SessionInterface.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("IsPlayerInSession couldn't get the session interface!"));
bIsInSession = false;
return;
}
bIsInSession = SessionInterface->IsPlayerInSession(NAME_GameSession, *PlayerToCheck.GetUniqueNetId());
}
FSessionsSearchSetting UAdvancedSessionsLibrary::MakeLiteralSessionSearchProperty(FSessionPropertyKeyPair SessionSearchProperty, EOnlineComparisonOpRedux ComparisonOp)
{
FSessionsSearchSetting setting;
setting.PropertyKeyPair = SessionSearchProperty;
setting.ComparisonOp = ComparisonOp;
return setting;
}
FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyByte(FName Key, uint8 Value)
{
FSessionPropertyKeyPair Prop;
Prop.Key = Key;
Prop.Data.SetValue((int32)Value);
return Prop;
}
FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyBool(FName Key, bool Value)
{
FSessionPropertyKeyPair Prop;
Prop.Key = Key;
Prop.Data.SetValue(Value);
return Prop;
}
FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyString(FName Key, FString Value)
{
FSessionPropertyKeyPair Prop;
Prop.Key = Key;
Prop.Data.SetValue(Value);
return Prop;
}
FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyInt(FName Key, int32 Value)
{
FSessionPropertyKeyPair Prop;
Prop.Key = Key;
Prop.Data.SetValue(Value);
return Prop;
}
FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyFloat(FName Key, float Value)
{
FSessionPropertyKeyPair Prop;
Prop.Key = Key;
Prop.Data.SetValue(Value);
return Prop;
}
void UAdvancedSessionsLibrary::GetSessionPropertyByte(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, uint8 &SettingValue)
{
for (FSessionPropertyKeyPair itr : ExtraSettings)
{
if (itr.Key == SettingName)
{
if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Int32)
{
int32 Val;
itr.Data.GetValue(Val);
SettingValue = (uint8)(Val);
SearchResult = ESessionSettingSearchResult::Found;
}
else
{
SearchResult = ESessionSettingSearchResult::WrongType;
}
return;
}
}
SearchResult = ESessionSettingSearchResult::NotFound;
return;
}
void UAdvancedSessionsLibrary::GetSessionPropertyBool(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, bool &SettingValue)
{
for (FSessionPropertyKeyPair itr : ExtraSettings)
{
if (itr.Key == SettingName)
{
if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Bool)
{
itr.Data.GetValue(SettingValue);
SearchResult = ESessionSettingSearchResult::Found;
}
else
{
SearchResult = ESessionSettingSearchResult::WrongType;
}
return;
}
}
SearchResult = ESessionSettingSearchResult::NotFound;
return;
}
void UAdvancedSessionsLibrary::GetSessionPropertyString(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, FString &SettingValue)
{
for (FSessionPropertyKeyPair itr : ExtraSettings)
{
if (itr.Key == SettingName)
{
if (itr.Data.GetType() == EOnlineKeyValuePairDataType::String)
{
itr.Data.GetValue(SettingValue);
SearchResult = ESessionSettingSearchResult::Found;
}
else
{
SearchResult = ESessionSettingSearchResult::WrongType;
}
return;
}
}
SearchResult = ESessionSettingSearchResult::NotFound;
return;
}
void UAdvancedSessionsLibrary::GetSessionPropertyInt(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, int32 &SettingValue)
{
for (FSessionPropertyKeyPair itr : ExtraSettings)
{
if (itr.Key == SettingName)
{
if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Int32)
{
itr.Data.GetValue(SettingValue);
SearchResult = ESessionSettingSearchResult::Found;
}
else
{
SearchResult = ESessionSettingSearchResult::WrongType;
}
return;
}
}
SearchResult = ESessionSettingSearchResult::NotFound;
return;
}
void UAdvancedSessionsLibrary::GetSessionPropertyFloat(const TArray<FSessionPropertyKeyPair> & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, float &SettingValue)
{
for (FSessionPropertyKeyPair itr : ExtraSettings)
{
if (itr.Key == SettingName)
{
if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Float)
{
itr.Data.GetValue(SettingValue);
SearchResult = ESessionSettingSearchResult::Found;
}
else
{
SearchResult = ESessionSettingSearchResult::WrongType;
}
return;
}
}
SearchResult = ESessionSettingSearchResult::NotFound;
return;
}
bool UAdvancedSessionsLibrary::HasOnlineSubsystem(FName SubSystemName)
{
return IOnlineSubsystem::DoesInstanceExist(SubSystemName);
}
void UAdvancedSessionsLibrary::GetNetPlayerIndex(APlayerController *PlayerController, int32 &NetPlayerIndex)
{
if (!PlayerController)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetNetPlayerIndex received a bad PlayerController!"));
NetPlayerIndex = 0;
return;
}
NetPlayerIndex = PlayerController->NetPlayerIndex;
return;
}
void UAdvancedSessionsLibrary::UniqueNetIdToString(const FBPUniqueNetId& UniqueNetId, FString &String)
{
const FUniqueNetId * ID = UniqueNetId.GetUniqueNetId();
if ( !ID )
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("UniqueNetIdToString received a bad UniqueNetId!"));
String = "ERROR, BAD UNIQUE NET ID";
}
else
String = ID->ToString();
}
void UAdvancedSessionsLibrary::GetUniqueNetID(APlayerController *PlayerController, FBPUniqueNetId &UniqueNetId)
{
if (!PlayerController)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromController received a bad PlayerController!"));
return;
}
if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
{
UniqueNetId.SetUniqueNetId(PlayerState->GetUniqueId().GetUniqueNetId());
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromController couldn't get the player uniquenetid!"));
}
return;
}
}
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)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromPlayerState received a bad PlayerState!"));
return;
}
UniqueNetId.SetUniqueNetId(PlayerState->GetUniqueId().GetUniqueNetId());
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromPlayerState couldn't get the player uniquenetid!"));
}
return;
}
bool UAdvancedSessionsLibrary::IsValidUniqueNetID(const FBPUniqueNetId &UniqueNetId)
{
return UniqueNetId.IsValid();
}
bool UAdvancedSessionsLibrary::EqualEqual_UNetIDUnetID(const FBPUniqueNetId &A, const FBPUniqueNetId &B)
{
return ((A.IsValid() && B.IsValid()) && (*A.GetUniqueNetId() == *B.GetUniqueNetId()));
}
FUniqueNetIdRepl UAdvancedSessionsLibrary::Conv_BPUniqueIDToUniqueNetIDRepl(const FBPUniqueNetId& InUniqueID)
{
return FUniqueNetIdRepl(InUniqueID.GetUniqueNetId()->AsShared());
}
void UAdvancedSessionsLibrary::SetPlayerName(APlayerController *PlayerController, FString PlayerName)
{
if (!PlayerController)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("SetLocalPlayerNameFromController Bad Player Controller!"));
return;
}
if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
{
PlayerState->SetPlayerName(PlayerName);
return;
}
else
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("SetLocalPlayerNameFromController had a bad player state!"));
}
}
void UAdvancedSessionsLibrary::GetPlayerName(APlayerController *PlayerController, FString &PlayerName)
{
if (!PlayerController)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetLocalPlayerNameFromController Bad Player Controller!"));
return;
}
if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
{
PlayerName = PlayerState->GetPlayerName();
return;
}
else
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetLocalPlayerNameFromController had a bad player state!"));
}
}
void UAdvancedSessionsLibrary::GetNumberOfNetworkPlayers(UObject* WorldContextObject, int32 &NumNetPlayers)
{
//Get World
UWorld* TheWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!TheWorld)
{
UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetNumberOfNetworkPlayers Failed to get World()!"));
return;
}
NumNetPlayers = TheWorld->GetGameState()->PlayerArray.Num();
}
bool UAdvancedSessionsLibrary::ServerTravel(UObject* WorldContextObject, const FString& FURL, bool bAbsolute, bool bShouldSkipGameNotify)
{
if (!WorldContextObject)
{
return false;
}
//using a context object to get the world
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
if (World)
{
return World->ServerTravel(FURL, bAbsolute, bShouldSkipGameNotify);
}
return false;
}

View File

@@ -0,0 +1,359 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedVoiceLibrary.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedVoiceLog);
void UAdvancedVoiceLibrary::IsHeadsetPresent(UObject* WorldContextObject, bool & bHasHeadset, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
bHasHeadset = false;
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Check For Headset couldn't get the voice interface!"));
return;
}
bHasHeadset = VoiceInterface->IsHeadsetPresent(LocalPlayerNum);
}
void UAdvancedVoiceLibrary::StartNetworkedVoice(UObject* WorldContextObject, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Start Networked Voice couldn't get the voice interface!"));
return;
}
VoiceInterface->StartNetworkedVoice(LocalPlayerNum);
}
void UAdvancedVoiceLibrary::StopNetworkedVoice(UObject* WorldContextObject, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Start Networked Voice couldn't get the voice interface!"));
return;
}
VoiceInterface->StopNetworkedVoice(LocalPlayerNum);
}
bool UAdvancedVoiceLibrary::RegisterLocalTalker(UObject* WorldContextObject, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Local Talker couldn't get the voice interface!"));
return false;
}
return VoiceInterface->RegisterLocalTalker(LocalPlayerNum);
}
void UAdvancedVoiceLibrary::RegisterAllLocalTalkers(UObject* WorldContextObject)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Local Talkers couldn't get the voice interface!"));
return;
}
VoiceInterface->RegisterLocalTalkers();
}
void UAdvancedVoiceLibrary::UnRegisterLocalTalker(UObject* WorldContextObject, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unregister Local Talker couldn't get the voice interface!"));
return;
}
VoiceInterface->UnregisterLocalTalker(LocalPlayerNum);
}
void UAdvancedVoiceLibrary::UnRegisterAllLocalTalkers(UObject* WorldContextObject)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister All Local Talkers couldn't get the voice interface!"));
return;
}
VoiceInterface->UnregisterLocalTalkers();
}
bool UAdvancedVoiceLibrary::RegisterRemoteTalker(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Remote Talker was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Remote Talker couldn't get the voice interface!"));
return false;
}
return VoiceInterface->RegisterRemoteTalker(*UniqueNetId.GetUniqueNetId());
}
bool UAdvancedVoiceLibrary::UnRegisterRemoteTalker(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister Remote Talker was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister Remote Talker couldn't get the voice interface!"));
return false;
}
return VoiceInterface->UnregisterRemoteTalker(*UniqueNetId.GetUniqueNetId());
}
void UAdvancedVoiceLibrary::RemoveAllRemoteTalkers(UObject* WorldContextObject)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Remove All Remote Talkers couldn't get the voice interface!"));
return;
}
VoiceInterface->RemoveAllRemoteTalkers();
}
bool UAdvancedVoiceLibrary::IsLocalPlayerTalking(UObject* WorldContextObject, uint8 LocalPlayerNum)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Local Player Talking couldn't get the voice interface!"));
return false;
}
return VoiceInterface->IsLocalPlayerTalking(LocalPlayerNum);
}
bool UAdvancedVoiceLibrary::IsRemotePlayerTalking(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Remote Player Talking was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Remote Player Talking couldn't get the voice interface!"));
return false;
}
return VoiceInterface->IsRemotePlayerTalking(*UniqueNetId.GetUniqueNetId());
}
bool UAdvancedVoiceLibrary::IsPlayerMuted(UObject* WorldContextObject, uint8 LocalUserNumChecking, const FBPUniqueNetId& UniqueNetId)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Player Muted was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Player Muted couldn't get the voice interface!"));
return false;
}
return VoiceInterface->IsMuted(LocalUserNumChecking, *UniqueNetId.GetUniqueNetId());
}
bool UAdvancedVoiceLibrary::MuteRemoteTalker(UObject* WorldContextObject, uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Mute Remote Talker was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Mute Remote Talker couldn't get the voice interface!"));
return false;
}
return VoiceInterface->MuteRemoteTalker(LocalUserNum, *UniqueNetId.GetUniqueNetId(), bIsSystemWide);
}
bool UAdvancedVoiceLibrary::UnMuteRemoteTalker(UObject* WorldContextObject, uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide)
{
if (!UniqueNetId.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker was passed an invalid unique net id!"));
return false;
}
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return false;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker couldn't get the voice interface!"));
return false;
}
return VoiceInterface->UnmuteRemoteTalker(LocalUserNum, *UniqueNetId.GetUniqueNetId(), bIsSystemWide);
}
void UAdvancedVoiceLibrary::GetNumLocalTalkers(UObject* WorldContextObject, int32 & NumLocalTalkers)
{
UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (!IsValid(World))
{
return;
}
IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(World);
if (!VoiceInterface.IsValid())
{
NumLocalTalkers = 0;
UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker couldn't get the voice interface!"));
return;
}
NumLocalTalkers = VoiceInterface->GetNumLocalTalkers();
}

View File

@@ -0,0 +1,91 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "AutoLoginUserCallbackProxy.h"
#include "Kismet/GameplayStatics.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// ULoginUserCallbackProxy
UAutoLoginUserCallbackProxy::UAutoLoginUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnLoginCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
{
}
UAutoLoginUserCallbackProxy* UAutoLoginUserCallbackProxy::AutoLoginUser(UObject* WorldContextObject, int32 LocalUserNum)
{
UAutoLoginUserCallbackProxy* Proxy = NewObject<UAutoLoginUserCallbackProxy>();
Proxy->LocalUserNumber = LocalUserNum;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UAutoLoginUserCallbackProxy::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("AutoLoginUser"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
DelegateHandle = Identity->AddOnLoginCompleteDelegate_Handle(LocalUserNumber, Delegate);
Identity->AutoLogin(LocalUserNumber);
return;
}
}
// Fail immediately
OnFailure.Broadcast();
}
void UAutoLoginUserCallbackProxy::OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("AutoLoginUser"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
Identity->ClearOnLoginCompleteDelegate_Handle(LocalUserNum, DelegateHandle);
}
if (APlayerController* PController = UGameplayStatics::GetPlayerController(WorldContextObject->GetWorld(), LocalUserNum))
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PController->Player);
FUniqueNetIdRepl uniqueId(UserId.AsShared());
if (Player)
{
Player->SetCachedUniqueNetId(uniqueId);
}
if (APlayerState* State = PController->PlayerState)
{
// Update UniqueId. See also ShowLoginUICallbackProxy.cpp
State->SetUniqueId(uniqueId);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
return;
}
OnFailure.Broadcast();
}

View File

@@ -0,0 +1,70 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "CancelFindSessionsCallbackProxy.h"
//////////////////////////////////////////////////////////////////////////
// UCancelFindSessionsCallbackProxy
UCancelFindSessionsCallbackProxy::UCancelFindSessionsCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnCancelFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
{
}
UCancelFindSessionsCallbackProxy* UCancelFindSessionsCallbackProxy::CancelFindSessions(UObject* WorldContextObject, class APlayerController* PlayerController)
{
UCancelFindSessionsCallbackProxy* Proxy = NewObject<UCancelFindSessionsCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UCancelFindSessionsCallbackProxy::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CancelFindSessions"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
DelegateHandle = Sessions->AddOnCancelFindSessionsCompleteDelegate_Handle(Delegate);
Sessions->CancelFindSessions();
// OnCompleted will get called, nothing more to do now
return;
}
else
{
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
}
// Fail immediately
OnFailure.Broadcast();
}
void UCancelFindSessionsCallbackProxy::OnCompleted(bool bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CancelFindSessionsCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnCancelFindSessionsCompleteDelegate_Handle(DelegateHandle);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,182 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "CreateSessionCallbackProxyAdvanced.h"
//////////////////////////////////////////////////////////////////////////
// UCreateSessionCallbackProxyAdvanced
UCreateSessionCallbackProxyAdvanced::UCreateSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, CreateCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateCompleted))
, StartCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartCompleted))
, NumPublicConnections(1)
{
}
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;
Proxy->NumPublicConnections = PublicConnections;
Proxy->NumPrivateConnections = PrivateConnections;
Proxy->bUseLAN = bUseLAN;
Proxy->WorldContextObject = WorldContextObject;
Proxy->bAllowInvites = bAllowInvites;
Proxy->ExtraSettings = ExtraSettings;
Proxy->bDedicatedServer = bIsDedicatedServer;
/*Proxy->bUsePresence = bUsePresence;*/
Proxy->bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
Proxy->bAllowJoinViaPresence = bAllowJoinViaPresence;
Proxy->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
Proxy->bAntiCheatProtected = bAntiCheatProtected;
Proxy->bUsesStats = bUsesStats;
Proxy->bShouldAdvertise = bShouldAdvertise;
Proxy->bUseLobbiesVoiceChatIfAvailable = bUseLobbiesVoiceChatIfAvailable;
Proxy->bStartAfterCreate = bStartAfterCreate;
return Proxy;
}
void UCreateSessionCallbackProxyAdvanced::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CreateSession"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (PlayerControllerWeakPtr.IsValid() )
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.OnlineSub != nullptr)
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
CreateCompleteDelegateHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegate);
FOnlineSessionSettings Settings;
Settings.NumPublicConnections = NumPublicConnections;
Settings.NumPrivateConnections = NumPrivateConnections;
Settings.bShouldAdvertise = bShouldAdvertise;
Settings.bAllowJoinInProgress = true;
Settings.bIsLANMatch = bUseLAN;
Settings.bAllowJoinViaPresence = bAllowJoinViaPresence;
Settings.bIsDedicated = bDedicatedServer;
if (bDedicatedServer)
{
Settings.bUseLobbiesIfAvailable = false;
Settings.bUsesPresence = false;
}
else
{
Settings.bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
Settings.bUsesPresence = bUseLobbiesIfAvailable;
}
Settings.bUseLobbiesVoiceChatIfAvailable = bUseLobbiesIfAvailable ? bUseLobbiesVoiceChatIfAvailable : false;
Settings.bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
Settings.bAntiCheatProtected = bAntiCheatProtected;
Settings.bUsesStats = bUsesStats;
// These are about the only changes over the standard Create Sessions Node
Settings.bAllowInvites = bAllowInvites;
FOnlineSessionSetting ExtraSetting;
for (int i = 0; i < ExtraSettings.Num(); i++)
{
ExtraSetting.Data = ExtraSettings[i].Data;
// ViaOnlineServiceAndPing
ExtraSetting.AdvertisementType = EOnlineDataAdvertisementType::ViaOnlineService;
Settings.Settings.Add(ExtraSettings[i].Key, ExtraSetting);
}
if (!bDedicatedServer )
{
if (PlayerControllerWeakPtr.IsValid() && Helper.UserID.IsValid())
{
Sessions->CreateSession(*Helper.UserID, NAME_GameSession, Settings);
}
else
{
FFrame::KismetExecutionMessage(TEXT("Invalid Player controller when attempting to start a session"), ELogVerbosity::Warning);
Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegateHandle);
// Fail immediately
OnFailure.Broadcast();
}
}
else
Sessions->CreateSession(0, NAME_GameSession, Settings);
// OnCreateCompleted will get called, nothing more to do now
return;
}
else
{
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
}
// Fail immediately
OnFailure.Broadcast();
}
void UCreateSessionCallbackProxyAdvanced::OnCreateCompleted(FName SessionName, bool bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CreateSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
//Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.OnlineSub != nullptr)
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegateHandle);
if (bWasSuccessful)
{
if (this->bStartAfterCreate)
{
UE_LOG_ONLINE_SESSION(Display, TEXT("Session creation completed. Automatic start is turned on, starting session now."));
StartCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(StartCompleteDelegate);
Sessions->StartSession(NAME_GameSession); // We'll call `OnSuccess.Broadcast()` when start succeeds.
}
else
{
UE_LOG_ONLINE_SESSION(Display, TEXT("Session creation completed. Automatic start is turned off, to start the session call 'StartSession'."));
OnSuccess.Broadcast();
}
// OnStartCompleted will get called, nothing more to do now
return;
}
}
}
if (!bWasSuccessful)
{
OnFailure.Broadcast();
}
}
void UCreateSessionCallbackProxyAdvanced::OnStartCompleted(FName SessionName, bool bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("StartSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
//Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.OnlineSub != nullptr)
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnStartSessionCompleteDelegate_Handle(StartCompleteDelegateHandle);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,78 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "EndSessionCallbackProxy.h"
//////////////////////////////////////////////////////////////////////////
// UEndSessionCallbackProxy
UEndSessionCallbackProxy::UEndSessionCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnEndSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
{
}
UEndSessionCallbackProxy* UEndSessionCallbackProxy::EndSession(UObject* WorldContextObject, class APlayerController* PlayerController)
{
UEndSessionCallbackProxy* Proxy = NewObject<UEndSessionCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UEndSessionCallbackProxy::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSession"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
FNamedOnlineSession* Session = Sessions->GetNamedSession(NAME_GameSession);
if (Session &&
Session->SessionState == EOnlineSessionState::InProgress)
{
DelegateHandle = Sessions->AddOnEndSessionCompleteDelegate_Handle(Delegate);
Sessions->EndSession(NAME_GameSession);
}
else
{
OnSuccess.Broadcast();
}
// OnCompleted will get called, nothing more to do now
return;
}
else
{
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
}
// Fail immediately
OnFailure.Broadcast();
}
void UEndSessionCallbackProxy::OnCompleted(FName SessionName, bool bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnEndSessionCompleteDelegate_Handle(DelegateHandle);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,138 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "FindFriendSessionCallbackProxy.h"
//////////////////////////////////////////////////////////////////////////
// UGetRecentPlayersCallbackProxy
DEFINE_LOG_CATEGORY(AdvancedFindFriendSessionLog);
UFindFriendSessionCallbackProxy::UFindFriendSessionCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, OnFindFriendSessionCompleteDelegate(FOnFindFriendSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnFindFriendSessionCompleted))
{
}
UFindFriendSessionCallbackProxy* UFindFriendSessionCallbackProxy::FindFriendSession(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId)
{
UFindFriendSessionCallbackProxy* Proxy = NewObject<UFindFriendSessionCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->cUniqueNetId = FriendUniqueNetId;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UFindFriendSessionCallbackProxy::Activate()
{
if (!cUniqueNetId.IsValid())
{
// Fail immediately
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed received a bad UniqueNetId!"));
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
return;
}
if (!PlayerControllerWeakPtr.IsValid())
{
// Fail immediately
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed received a bad playercontroller!"));
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (!Helper.IsValid())
{
// Fail immediately
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
return;
}
IOnlineSessionPtr Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
if (!Player)
{
// Fail immediately
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed couldn't cast to ULocalPlayer!"));
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
return;
}
FindFriendSessionCompleteDelegateHandle = Sessions->AddOnFindFriendSessionCompleteDelegate_Handle(Player->GetControllerId(), OnFindFriendSessionCompleteDelegate);
Sessions->FindFriendSession(Player->GetControllerId(), *cUniqueNetId.GetUniqueNetId());
return;
}
// Fail immediately
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
}
void UFindFriendSessionCallbackProxy::OnFindFriendSessionCompleted(int32 LocalPlayer, bool bWasSuccessful, const TArray<FOnlineSessionSearchResult>& SessionInfo)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
IOnlineSessionPtr Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
Sessions->ClearOnFindFriendSessionCompleteDelegate_Handle(LocalPlayer, FindFriendSessionCompleteDelegateHandle);
if (bWasSuccessful)
{
TArray<FBlueprintSessionResult> Result;
for (auto& Sesh : SessionInfo)
{
if (Sesh.IsValid())
{
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);
}
}
if (Result.Num() > 0)
OnSuccess.Broadcast(Result);
else
{
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed, returned an invalid session."));
OnFailure.Broadcast(Result);
}
}
else
{
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed"));
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
}
}
else
{
UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed"));
TArray<FBlueprintSessionResult> EmptyResult;
OnFailure.Broadcast(EmptyResult);
}
}

View File

@@ -0,0 +1,451 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "FindSessionsCallbackProxyAdvanced.h"
#include "Online/OnlineSessionNames.h"
//////////////////////////////////////////////////////////////////////////
// UFindSessionsCallbackProxyAdvanced
UFindSessionsCallbackProxyAdvanced::UFindSessionsCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
, bUseLAN(false)
{
bRunSecondSearch = false;
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* Proxy = NewObject<UFindSessionsCallbackProxyAdvanced>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->bUseLAN = bUseLAN;
Proxy->MaxResults = MaxResults;
Proxy->WorldContextObject = WorldContextObject;
Proxy->SearchSettings = Filters;
Proxy->ServerSearchType = ServerTypeToSearch;
Proxy->bEmptyServersOnly = bEmptyServersOnly,
Proxy->bNonEmptyServersOnly = bNonEmptyServersOnly;
Proxy->bSecureServersOnly = bSecureServersOnly;
//Proxy->bSearchLobbies = bSearchLobbies;
Proxy->MinSlotsAvailable = MinSlotsAvailable;
return Proxy;
}
void UFindSessionsCallbackProxyAdvanced::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("FindSessions"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
// Re-initialize here, otherwise I think there might be issues with people re-calling search for some reason before it is destroyed
bRunSecondSearch = false;
bIsOnSecondSearch = false;
DelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(Delegate);
SearchObject = MakeShareable(new FOnlineSessionSearch);
SearchObject->MaxSearchResults = MaxResults;
SearchObject->bIsLanQuery = bUseLAN;
//SearchObject->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
// Create temp filter variable, because I had to re-define a blueprint version of this, it is required.
FOnlineSearchSettingsEx tem;
/* // Search only for dedicated servers (value is true/false)
#define SEARCH_DEDICATED_ONLY FName(TEXT("DEDICATEDONLY"))
// Search for empty servers only (value is true/false)
#define SEARCH_EMPTY_SERVERS_ONLY FName(TEXT("EMPTYONLY"))
// Search for non empty servers only (value is true/false)
#define SEARCH_NONEMPTY_SERVERS_ONLY FName(TEXT("NONEMPTYONLY"))
// Search for secure servers only (value is true/false)
#define SEARCH_SECURE_SERVERS_ONLY FName(TEXT("SECUREONLY"))
// Search for presence sessions only (value is true/false)
#define SEARCH_PRESENCE FName(TEXT("PRESENCESEARCH"))
// Search for a match with min player availability (value is int)
#define SEARCH_MINSLOTSAVAILABLE FName(TEXT("MINSLOTSAVAILABLE"))
// Exclude all matches where any unique ids in a given array are present (value is string of the form "uniqueid1;uniqueid2;uniqueid3")
#define SEARCH_EXCLUDE_UNIQUEIDS FName(TEXT("EXCLUDEUNIQUEIDS"))
// User ID to search for session of
#define SEARCH_USER FName(TEXT("SEARCHUSER"))
// Keywords to match in session search
#define SEARCH_KEYWORDS FName(TEXT("SEARCHKEYWORDS"))*/
/** Keywords to match in session search */
/** The matchmaking queue name to matchmake in, e.g. "TeamDeathmatch" (value is string) */
/** #define SEARCH_MATCHMAKING_QUEUE FName(TEXT("MATCHMAKINGQUEUE"))*/
/** If set, use the named Xbox Live hopper to find a session via matchmaking (value is a string) */
/** #define SEARCH_XBOX_LIVE_HOPPER_NAME FName(TEXT("LIVEHOPPERNAME"))*/
/** Which session template from the service configuration to use */
/** #define SEARCH_XBOX_LIVE_SESSION_TEMPLATE_NAME FName(TEXT("LIVESESSIONTEMPLATE"))*/
/** Selection method used to determine which match to join when multiple are returned (valid only on Switch) */
/** #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);
if (bNonEmptyServersOnly)
tem.Set(SEARCH_NONEMPTY_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
if (bSecureServersOnly)
tem.Set(SEARCH_SECURE_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
if (MinSlotsAvailable != 0)
tem.Set(SEARCH_MINSLOTSAVAILABLE, MinSlotsAvailable, EOnlineComparisonOp::GreaterThanEquals);
// Filter results
if (SearchSettings.Num() > 0)
{
for (int i = 0; i < SearchSettings.Num(); i++)
{
// Function that was added to make directly adding a FVariant possible
tem.HardSet(SearchSettings[i].PropertyKeyPair.Key, SearchSettings[i].PropertyKeyPair.Data, SearchSettings[i].ComparisonOp);
}
}
switch (ServerSearchType)
{
case EBPServerPresenceSearchType::ClientServersOnly:
{
//tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
//if (bSearchLobbies)// && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
}
break;
case EBPServerPresenceSearchType::DedicatedServersOnly:
{
//tem.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals);
}
break;
case EBPServerPresenceSearchType::AllServers:
default:
{
//if (IOnlineSubsystem::DoesInstanceExist("STEAM"))
//{
bRunSecondSearch = true;
SearchObjectDedicated = MakeShareable(new FOnlineSessionSearch);
SearchObjectDedicated->MaxSearchResults = MaxResults;
SearchObjectDedicated->bIsLanQuery = bUseLAN;
FOnlineSearchSettingsEx DedicatedOnly = tem;
//tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
//if (bSearchLobbies)// && !IOnlineSubsystem::DoesInstanceExist("STEAM"))
tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
//DedicatedOnly.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals);
SearchObjectDedicated->QuerySettings = DedicatedOnly;
//}
}
break;
}
// Copy the derived temp variable over to it's base class
SearchObject->QuerySettings = tem;
Sessions->FindSessions(*Helper.UserID, SearchObject.ToSharedRef());
// OnQueryCompleted will get called, nothing more to do now
return;
}
else
{
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
}
// Fail immediately
OnFailure.Broadcast(SessionSearchResults);
}
void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("FindSessionsCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (!Helper.IsValid())
{
// Fail immediately
OnFailure.Broadcast(SessionSearchResults);
return;
}
if (!bRunSecondSearch && Helper.IsValid())
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnFindSessionsCompleteDelegate_Handle(DelegateHandle);
}
}
if (bSuccess)
{
if (bIsOnSecondSearch)
{
if (SearchObjectDedicated.IsValid())
{
// Just log the results for now, will need to add a blueprint-compatible search result struct
for (auto& Result : SearchObjectDedicated->SearchResults)
{
FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs);
FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log);
FBlueprintSessionResult BPResult;
BPResult.OnlineResult = Result;
SessionSearchResults.AddUnique(BPResult);
}
OnSuccess.Broadcast(SessionSearchResults);
return;
}
}
else
{
if (SearchObject.IsValid())
{
// Just log the results for now, will need to add a blueprint-compatible search result struct
for (auto& Result : SearchObject->SearchResults)
{
FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs);
FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log);
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)
{
OnSuccess.Broadcast(SessionSearchResults);
return;
}
}
}
}
else
{
if (!bRunSecondSearch)
{
// Need to account for only one of the searches failing
if (SessionSearchResults.Num() > 0)
OnSuccess.Broadcast(SessionSearchResults);
else
OnFailure.Broadcast(SessionSearchResults);
return;
}
}
if (Helper.IsValid() && bRunSecondSearch && ServerSearchType == EBPServerPresenceSearchType::AllServers)
{
bRunSecondSearch = false;
bIsOnSecondSearch = true;
auto Sessions = Helper.OnlineSub->GetSessionInterface();
Sessions->FindSessions(*Helper.UserID, SearchObjectDedicated.ToSharedRef());
}
else // We lost our player controller
{
if (bSuccess && SessionSearchResults.Num() > 0)
OnSuccess.Broadcast(SessionSearchResults);
else
OnFailure.Broadcast(SessionSearchResults);
}
}
void UFindSessionsCallbackProxyAdvanced::FilterSessionResults(const TArray<FBlueprintSessionResult> &SessionResults, const TArray<FSessionsSearchSetting> &Filters, TArray<FBlueprintSessionResult> &FilteredResults)
{
for (int j = 0; j < SessionResults.Num(); j++)
{
bool bAddResult = true;
// Filter results
if (Filters.Num() > 0)
{
const FOnlineSessionSetting * setting;
for (int i = 0; i < Filters.Num(); i++)
{
setting = SessionResults[j].OnlineResult.Session.SessionSettings.Settings.Find(Filters[i].PropertyKeyPair.Key);
// Couldn't find this key
if (!setting)
continue;
if (!CompareVariants(setting->Data, Filters[i].PropertyKeyPair.Data, Filters[i].ComparisonOp))
{
bAddResult = false;
break;
}
}
}
if (bAddResult)
FilteredResults.Add(SessionResults[j]);
}
return;
}
bool UFindSessionsCallbackProxyAdvanced::CompareVariants(const FVariantData &A, const FVariantData &B, EOnlineComparisonOpRedux Comparator)
{
if (A.GetType() != B.GetType())
return false;
switch (A.GetType())
{
case EOnlineKeyValuePairDataType::Bool:
{
bool bA, bB;
A.GetValue(bA);
B.GetValue(bB);
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
default:
return false;break;
}
}
case EOnlineKeyValuePairDataType::Double:
{
double bA, bB;
A.GetValue(bA);
B.GetValue(bB);
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
case EOnlineComparisonOpRedux::GreaterThanEquals:
return (bA == bB || bA > bB); break;
case EOnlineComparisonOpRedux::LessThanEquals:
return (bA == bB || bA < bB); break;
case EOnlineComparisonOpRedux::GreaterThan:
return bA > bB; break;
case EOnlineComparisonOpRedux::LessThan:
return bA < bB; break;
default:
return false; break;
}
}
case EOnlineKeyValuePairDataType::Float:
{
float tbA, tbB;
double bA, bB;
A.GetValue(tbA);
B.GetValue(tbB);
bA = (double)tbA;
bB = (double)tbB;
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
case EOnlineComparisonOpRedux::GreaterThanEquals:
return (bA == bB || bA > bB); break;
case EOnlineComparisonOpRedux::LessThanEquals:
return (bA == bB || bA < bB); break;
case EOnlineComparisonOpRedux::GreaterThan:
return bA > bB; break;
case EOnlineComparisonOpRedux::LessThan:
return bA < bB; break;
default:
return false; break;
}
}
case EOnlineKeyValuePairDataType::Int32:
{
int32 bA, bB;
A.GetValue(bA);
B.GetValue(bB);
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
case EOnlineComparisonOpRedux::GreaterThanEquals:
return (bA == bB || bA > bB); break;
case EOnlineComparisonOpRedux::LessThanEquals:
return (bA == bB || bA < bB); break;
case EOnlineComparisonOpRedux::GreaterThan:
return bA > bB; break;
case EOnlineComparisonOpRedux::LessThan:
return bA < bB; break;
default:
return false; break;
}
}
case EOnlineKeyValuePairDataType::Int64:
{
uint64 bA, bB;
A.GetValue(bA);
B.GetValue(bB);
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
case EOnlineComparisonOpRedux::GreaterThanEquals:
return (bA == bB || bA > bB); break;
case EOnlineComparisonOpRedux::LessThanEquals:
return (bA == bB || bA < bB); break;
case EOnlineComparisonOpRedux::GreaterThan:
return bA > bB; break;
case EOnlineComparisonOpRedux::LessThan:
return bA < bB; break;
default:
return false; break;
}
}
case EOnlineKeyValuePairDataType::String:
{
FString bA, bB;
A.GetValue(bA);
B.GetValue(bB);
switch (Comparator)
{
case EOnlineComparisonOpRedux::Equals:
return bA == bB; break;
case EOnlineComparisonOpRedux::NotEquals:
return bA != bB; break;
default:
return false; break;
}
}
case EOnlineKeyValuePairDataType::Empty:
case EOnlineKeyValuePairDataType::Blob:
default:
return false; break;
}
}

View File

@@ -0,0 +1,114 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "GetFriendsCallbackProxy.h"
#include "Online.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
//////////////////////////////////////////////////////////////////////////
// UGetFriendsCallbackProxy
DEFINE_LOG_CATEGORY(AdvancedGetFriendsLog);
UGetFriendsCallbackProxy::UGetFriendsCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, FriendListReadCompleteDelegate(FOnReadFriendsListComplete::CreateUObject(this, &ThisClass::OnReadFriendsListCompleted))
{
}
UGetFriendsCallbackProxy* UGetFriendsCallbackProxy::GetAndStoreFriendsList(UObject* WorldContextObject, class APlayerController* PlayerController)
{
UGetFriendsCallbackProxy* Proxy = NewObject<UGetFriendsCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UGetFriendsCallbackProxy::Activate()
{
if (!PlayerControllerWeakPtr.IsValid())
{
// Fail immediately
UE_LOG(AdvancedGetFriendsLog, Warning, TEXT("GetFriends Failed received a bad player controller!"));
TArray<FBPFriendInfo> EmptyArray;
OnFailure.Broadcast(EmptyArray);
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetFriends"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (Helper.IsValid())
{
IOnlineFriendsPtr Friends = Helper.OnlineSub->GetFriendsInterface();
if (Friends.IsValid())
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
Friends->ReadFriendsList(Player->GetControllerId(), EFriendsLists::ToString((EFriendsLists::Default)), FriendListReadCompleteDelegate);
return;
}
}
// Fail immediately
TArray<FBPFriendInfo> EmptyArray;
OnFailure.Broadcast(EmptyArray);
}
void UGetFriendsCallbackProxy::OnReadFriendsListCompleted(int32 LocalUserNum, bool bWasSuccessful, const FString& ListName, const FString& ErrorString)
{
if (bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetFriends"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
if (!Helper.IsValid())
{
TArray<FBPFriendInfo> EmptyArray;
OnFailure.Broadcast(EmptyArray);
return;
}
auto Friends = Helper.OnlineSub->GetFriendsInterface();
if (Friends.IsValid())
{
// Not actually needed anymore, plus was not being validated and causing a crash
//ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
TArray<FBPFriendInfo> FriendsListOut;
TArray< TSharedRef<FOnlineFriend> > FriendList;
if (Friends->GetFriendsList(LocalUserNum, ListName, FriendList))
{
for (int32 i = 0; i < FriendList.Num(); i++)
{
FBPFriendInfo BPF;
const FOnlineUserPresence& pres = FriendList[i]->GetPresence();
BPF.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
BPF.DisplayName = FriendList[i]->GetDisplayName();
BPF.RealName = FriendList[i]->GetRealName();
BPF.UniqueNetId.SetUniqueNetId(FriendList[i]->GetUserId());
BPF.bIsPlayingSameGame = pres.bIsPlayingThisGame;
BPF.PresenceInfo.bIsOnline = pres.bIsOnline;
BPF.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
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
//BPF.PresenceInfo.StatusString = pres.Status.StatusStr;
BPF.PresenceInfo.bIsJoinable = pres.bIsJoinable;
BPF.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;
FriendsListOut.Add(BPF);
}
}
OnSuccess.Broadcast(FriendsListOut);
}
}
else
{
TArray<FBPFriendInfo> EmptyArray;
OnFailure.Broadcast(EmptyArray);
}
}

View File

@@ -0,0 +1,104 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "GetRecentPlayersCallbackProxy.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// UGetRecentPlayersCallbackProxy
DEFINE_LOG_CATEGORY(AdvancedGetRecentPlayersLog);
UGetRecentPlayersCallbackProxy::UGetRecentPlayersCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, QueryRecentPlayersCompleteDelegate(FOnQueryRecentPlayersCompleteDelegate::CreateUObject(this, &ThisClass::OnQueryRecentPlayersCompleted))
{
}
UGetRecentPlayersCallbackProxy* UGetRecentPlayersCallbackProxy::GetAndStoreRecentPlayersList(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId)
{
UGetRecentPlayersCallbackProxy* Proxy = NewObject<UGetRecentPlayersCallbackProxy>();
Proxy->cUniqueNetId = UniqueNetId;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UGetRecentPlayersCallbackProxy::Activate()
{
if (!cUniqueNetId.IsValid())
{
// Fail immediately
UE_LOG(AdvancedGetRecentPlayersLog, Warning, TEXT("GetRecentPlayers Failed received a bad UniqueNetId!"));
TArray<FBPOnlineRecentPlayer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetRecentPlayers"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
TArray<FBPOnlineRecentPlayer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
return;
}
IOnlineFriendsPtr Friends = Helper.OnlineSub->GetFriendsInterface();
if (Friends.IsValid())
{
DelegateHandle = Friends->AddOnQueryRecentPlayersCompleteDelegate_Handle(QueryRecentPlayersCompleteDelegate);
// Testing with null namespace
Friends->QueryRecentPlayers(*(cUniqueNetId.GetUniqueNetId()), "");
return;
}
// Fail immediately
TArray<FBPOnlineRecentPlayer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
}
void UGetRecentPlayersCallbackProxy::OnQueryRecentPlayersCompleted(const FUniqueNetId &UserID, const FString &Namespace, bool bWasSuccessful, const FString& ErrorString)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetRecentPlayers"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
TArray<FBPOnlineRecentPlayer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
return;
}
IOnlineFriendsPtr Friends = Helper.OnlineSub->GetFriendsInterface();
if (Friends.IsValid())
Friends->ClearOnQueryRecentPlayersCompleteDelegate_Handle(DelegateHandle);
if (bWasSuccessful)
{
// WHOOPS
//IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
if (Friends.IsValid())
{
TArray<FBPOnlineRecentPlayer> PlayersListOut;
TArray< TSharedRef<FOnlineRecentPlayer> > PlayerList;
Friends->GetRecentPlayers(*(cUniqueNetId.GetUniqueNetId()), "", PlayerList);
for (int32 i = 0; i < PlayerList.Num(); i++)
{
TSharedRef<FOnlineRecentPlayer> Player = PlayerList[i];
FBPOnlineRecentPlayer BPF;
BPF.DisplayName = Player->GetDisplayName();
BPF.RealName = Player->GetRealName();
BPF.UniqueNetId.SetUniqueNetId(Player->GetUserId());
PlayersListOut.Add(BPF);
}
OnSuccess.Broadcast(PlayersListOut);
}
}
else
{
TArray<FBPOnlineRecentPlayer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
}
}

View File

@@ -0,0 +1,48 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "GetUserPrivilegeCallbackProxy.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// UGetUserPrivilegeCallbackProxy
UGetUserPrivilegeCallbackProxy::UGetUserPrivilegeCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UGetUserPrivilegeCallbackProxy* UGetUserPrivilegeCallbackProxy::GetUserPrivilege(UObject* WorldContextObject, const EBPUserPrivileges & PrivilegeToCheck, const FBPUniqueNetId & PlayerUniqueNetID)
{
UGetUserPrivilegeCallbackProxy* Proxy = NewObject<UGetUserPrivilegeCallbackProxy>();
Proxy->PlayerUniqueNetID.SetUniqueNetId(PlayerUniqueNetID.GetUniqueNetId());
Proxy->UserPrivilege = PrivilegeToCheck;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UGetUserPrivilegeCallbackProxy::Activate()
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetUserPrivilege"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
Identity->GetUserPrivilege(*PlayerUniqueNetID.GetUniqueNetId(), (EUserPrivileges::Type)UserPrivilege, IOnlineIdentity::FOnGetUserPrivilegeCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted));
return;
}
// Fail immediately
OnFailure.Broadcast();
}
void UGetUserPrivilegeCallbackProxy::OnCompleted(const FUniqueNetId& PlayerID, EUserPrivileges::Type Privilege, uint32 PrivilegeResult)
{
OnSuccess.Broadcast(/*PlayerID,*/ (EBPUserPrivileges)Privilege, PrivilegeResult == 0);
}

View File

@@ -0,0 +1,111 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "LoginUserCallbackProxy.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// ULoginUserCallbackProxy
ULoginUserCallbackProxy::ULoginUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnLoginCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
{
}
ULoginUserCallbackProxy* ULoginUserCallbackProxy::LoginUser(UObject* WorldContextObject, class APlayerController* PlayerController, FString UserID, FString UserToken, FString AuthType)
{
ULoginUserCallbackProxy* Proxy = NewObject<ULoginUserCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->UserID = UserID;
Proxy->UserToken = UserToken;
Proxy->AuthType = AuthType;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void ULoginUserCallbackProxy::Activate()
{
if (!PlayerControllerWeakPtr.IsValid())
{
OnFailure.Broadcast();
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
if (!Player)
{
OnFailure.Broadcast();
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("LoginUser"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
// Fallback to default AuthType if nothing is specified
if (AuthType.IsEmpty())
{
AuthType = Identity->GetAuthType();
}
DelegateHandle = Identity->AddOnLoginCompleteDelegate_Handle(Player->GetControllerId(), Delegate);
FOnlineAccountCredentials AccountCreds(AuthType, UserID, UserToken);
Identity->Login(Player->GetControllerId(), AccountCreds);
return;
}
// Fail immediately
OnFailure.Broadcast();
}
void ULoginUserCallbackProxy::OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal)
{
if (PlayerControllerWeakPtr.IsValid())
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
FUniqueNetIdRepl UniqueID(UserId.AsShared());
if (Player)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("GetUserPrivilege"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
Identity->ClearOnLoginCompleteDelegate_Handle(Player->GetControllerId(), DelegateHandle);
}
Player->SetCachedUniqueNetId(UniqueID);
}
if (APlayerState* State = PlayerControllerWeakPtr->PlayerState)
{
// Update UniqueId. See also ShowLoginUICallbackProxy.cpp
State->SetUniqueId(UniqueID);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,94 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "LogoutUserCallbackProxy.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// ULogoutUserCallbackProxy
ULogoutUserCallbackProxy::ULogoutUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, Delegate(FOnLogoutCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
{
}
ULogoutUserCallbackProxy* ULogoutUserCallbackProxy::LogoutUser(UObject* WorldContextObject, class APlayerController* PlayerController)
{
ULogoutUserCallbackProxy* Proxy = NewObject<ULogoutUserCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void ULogoutUserCallbackProxy::Activate()
{
if (!PlayerControllerWeakPtr.IsValid())
{
OnFailure.Broadcast();
return;
}
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
if (!Player)
{
OnFailure.Broadcast();
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("LogoutUser"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
DelegateHandle = Identity->AddOnLogoutCompleteDelegate_Handle(Player->GetControllerId(), Delegate);
Identity->Logout(Player->GetControllerId());
return;
}
// Fail immediately
OnFailure.Broadcast();
}
void ULogoutUserCallbackProxy::OnCompleted(int LocalUserNum, bool bWasSuccessful)
{
if (PlayerControllerWeakPtr.IsValid())
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
if (Player)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("LogoutUser"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Identity = Helper.OnlineSub->GetIdentityInterface();
if (Identity.IsValid())
{
Identity->ClearOnLogoutCompleteDelegate_Handle(Player->GetControllerId(), DelegateHandle);
}
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,82 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "SendFriendInviteCallbackProxy.h"
#include "Online.h"
//////////////////////////////////////////////////////////////////////////
// UGetRecentPlayersCallbackProxy
DEFINE_LOG_CATEGORY(AdvancedSendFriendInviteLog);
USendFriendInviteCallbackProxy::USendFriendInviteCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, OnSendInviteCompleteDelegate(FOnSendInviteComplete::CreateUObject(this, &ThisClass::OnSendInviteComplete))
{
}
USendFriendInviteCallbackProxy* USendFriendInviteCallbackProxy::SendFriendInvite(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &UniqueNetIDInvited)
{
USendFriendInviteCallbackProxy* Proxy = NewObject<USendFriendInviteCallbackProxy>();
Proxy->PlayerControllerWeakPtr = PlayerController;
Proxy->cUniqueNetId = UniqueNetIDInvited;
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void USendFriendInviteCallbackProxy::Activate()
{
if (!cUniqueNetId.IsValid())
{
// Fail immediately
UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed received a bad UniqueNetId!"));
OnFailure.Broadcast();
return;
}
if (!PlayerControllerWeakPtr.IsValid())
{
// Fail immediately
UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed received a bad playercontroller!"));
OnFailure.Broadcast();
return;
}
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("SendFriendInvite"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (!Helper.OnlineSub)
{
OnFailure.Broadcast();
return;
}
auto Friends = Helper.OnlineSub->GetFriendsInterface();
if (Friends.IsValid())
{
ULocalPlayer* Player = Cast<ULocalPlayer>(PlayerControllerWeakPtr->Player);
if (!Player)
{
// Fail immediately
UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed couldn't cast to ULocalPlayer!"));
OnFailure.Broadcast();
return;
}
Friends->SendInvite(Player->GetControllerId(), *cUniqueNetId.GetUniqueNetId(), EFriendsLists::ToString((EFriendsLists::Default)), OnSendInviteCompleteDelegate);
return;
}
// Fail immediately
OnFailure.Broadcast();
}
void USendFriendInviteCallbackProxy::OnSendInviteComplete(int32 LocalPlayerNum, bool bWasSuccessful, const FUniqueNetId &InvitedPlayer, const FString &ListName, const FString &ErrorString)
{
if ( bWasSuccessful )
{
OnSuccess.Broadcast();
}
else
{
UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed with error: %s"), *ErrorString);
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,58 @@
#include "StartSessionCallbackProxyAdvanced.h"
UStartSessionCallbackProxyAdvanced::UStartSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, StartCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartCompleted))
{
}
UStartSessionCallbackProxyAdvanced* UStartSessionCallbackProxyAdvanced::StartAdvancedSession(
UObject* WorldContextObject)
{
UStartSessionCallbackProxyAdvanced* Proxy = NewObject<UStartSessionCallbackProxyAdvanced>();
Proxy->WorldContextObject = WorldContextObject;
return Proxy;
}
void UStartSessionCallbackProxyAdvanced::Activate()
{
const FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("StartSession"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
const auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
StartCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(StartCompleteDelegate);
Sessions->StartSession(NAME_GameSession);
return;
}
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
// Fail immediately
OnFailure.Broadcast();
}
void UStartSessionCallbackProxyAdvanced::OnStartCompleted(FName SessionName, bool bWasSuccessful)
{
FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("StartSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnStartSessionCompleteDelegate_Handle(StartCompleteDelegateHandle);
}
}
if (bWasSuccessful)
{
OnSuccess.Broadcast();
}
else
{
OnFailure.Broadcast();
}
}

View File

@@ -0,0 +1,135 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "UpdateSessionCallbackProxyAdvanced.h"
//////////////////////////////////////////////////////////////////////////
// UUpdateSessionCallbackProxyAdvanced
UUpdateSessionCallbackProxyAdvanced::UUpdateSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, OnUpdateSessionCompleteDelegate(FOnUpdateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnUpdateCompleted))
, NumPublicConnections(1)
{
}
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;
Proxy->NumPrivateConnections = PrivateConnections;
Proxy->bUseLAN = bUseLAN;
Proxy->WorldContextObject = WorldContextObject;
Proxy->bAllowInvites = bAllowInvites;
Proxy->ExtraSettings = ExtraSettings;
Proxy->bRefreshOnlineData = bRefreshOnlineData;
Proxy->bAllowJoinInProgress = bAllowJoinInProgress;
Proxy->bDedicatedServer = bIsDedicatedServer;
Proxy->bShouldAdvertise = bShouldAdvertise;
Proxy->bAllowJoinViaPresence = bAllowJoinViaPresence;
Proxy->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
return Proxy;
}
void UUpdateSessionCallbackProxyAdvanced::Activate()
{
const FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("UpdateSession"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
const auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
if (Sessions->GetNumSessions() < 1)
{
OnFailure.Broadcast();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("NO REGISTERED SESSIONS!"));
return;
}
// This gets the actual session itself
//FNamedOnlineSession * curSession = Sessions->GetNamedSession(NAME_GameSession);
FOnlineSessionSettings* Settings = Sessions->GetSessionSettings(NAME_GameSession);
if (!Settings)
{
// Fail immediately
OnFailure.Broadcast();
return;
}
OnUpdateSessionCompleteDelegateHandle = Sessions->AddOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegate);
// FOnlineSessionSettings Settings;
//Settings->BuildUniqueId = GetBuildUniqueId();
Settings->NumPublicConnections = NumPublicConnections;
Settings->NumPrivateConnections = NumPrivateConnections;
Settings->bShouldAdvertise = bShouldAdvertise;
Settings->bAllowJoinInProgress = bAllowJoinInProgress;
Settings->bIsLANMatch = bUseLAN;
//Settings->bUsesPresence = 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++)
{
fSetting = Settings->Settings.Find(ExtraSettings[i].Key);
if (fSetting)
{
fSetting->Data = ExtraSettings[i].Data;
}
else
{
ExtraSetting.Data = ExtraSettings[i].Data;
ExtraSetting.AdvertisementType = EOnlineDataAdvertisementType::ViaOnlineService;
Settings->Settings.Add(ExtraSettings[i].Key, ExtraSetting);
}
}
Sessions->UpdateSession(NAME_GameSession, *Settings, bRefreshOnlineData);
// OnUpdateCompleted will get called, nothing more to do now
return;
}
else
{
FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
}
}
// Fail immediately
OnFailure.Broadcast();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Sessions not supported"));
}
void UUpdateSessionCallbackProxyAdvanced::OnUpdateCompleted(FName SessionName, bool bWasSuccessful)
{
const FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("UpdateSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject.Get(), EGetWorldErrorMode::LogAndReturnNull));
if (Helper.OnlineSub != nullptr)
{
const auto Sessions = Helper.OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->ClearOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegateHandle);
if (bWasSuccessful)
{
OnSuccess.Broadcast();
return;
}
}
}
if (!bWasSuccessful)
{
OnFailure.Broadcast();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("WAS NOT SUCCESSFUL"));
}
}

View File

@@ -0,0 +1,42 @@
{
"FileVersion" : 3,
"FriendlyName" : "Advanced Steam Sessions",
"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",
"CreatedByURL" : "N/A",
"Modules" :
[
{
"Name": "AdvancedSteamSessions",
"Type": "RunTime",
"LoadingPhase": "PostDefault"
}
],
"Plugins": [
{
"Name": "AdvancedSessions",
"Enabled": true
},
{
"Name": "OnlineSubsystem",
"Enabled": true
},
{
"Name": "OnlineSubsystemSteam",
"Enabled": true
},
{
"Name": "OnlineSubsystemUtils",
"Enabled": true
},
{
"Name": "SteamShared",
"Enabled": true
}
]
}

View File

@@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,26 @@
using UnrealBuildTool;
using System.IO;
public class AdvancedSteamSessions : ModuleRules
{
public AdvancedSteamSessions(ReadOnlyTargetRules Target) : base(Target)
{
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
//bEnforceIWYU = true;
PublicDefinitions.Add("WITH_ADVANCED_STEAM_SESSIONS=1");
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OnlineSubsystem", "CoreUObject", "OnlineSubsystemUtils", "Networking", "Sockets", "AdvancedSessions"/*"Voice", "OnlineSubsystemSteam"*/ });
PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem", "Sockets", "Networking", "OnlineSubsystemUtils" /*"Voice", "Steamworks","OnlineSubsystemSteam"*/});
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

@@ -0,0 +1,389 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineFriendsInterface.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlineMessageInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Engine/GameInstance.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "BlueprintDataDefinitions.h"
#include "UObject/UObjectIterator.h"
// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
// disable the warnings locally. Remove when this is fixed in the SDK
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
// #TODO check back on this at some point
#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
#endif
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#pragma push_macro("ARRAY_COUNT")
#undef ARRAY_COUNT
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(push))
MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
#endif // USING_CODE_ANALYSIS
#include <steam/steam_api.h>
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(pop))
#endif // USING_CODE_ANALYSIS
#include <steam/isteamapps.h>
//#include <steam/isteamapplist.h>
//#include <OnlineSubsystemSteamTypes.h>
#pragma pop_macro("ARRAY_COUNT")
// @todo Steam: See above
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// Making a copy of this here since the original is still in a private folder and is screwing with things
/**
* Steam specific implementation of the unique net id
*/
class FUniqueNetIdSteam2 :
public FUniqueNetId
{
PACKAGE_SCOPE:
/** Holds the net id for a player */
uint64 UniqueNetId;
/** Hidden on purpose */
FUniqueNetIdSteam2() :
UniqueNetId(0)
{
}
/**
* Copy Constructor
*
* @param Src the id to copy
*/
explicit FUniqueNetIdSteam2(const FUniqueNetIdSteam2& Src) :
UniqueNetId(Src.UniqueNetId)
{
}
public:
/**
* Constructs this object with the specified net id
*
* @param InUniqueNetId the id to set ours to
*/
explicit FUniqueNetIdSteam2(uint64 InUniqueNetId) :
UniqueNetId(InUniqueNetId)
{
}
/**
* Constructs this object with the steam id
*
* @param InUniqueNetId the id to set ours to
*/
explicit FUniqueNetIdSteam2(CSteamID InSteamId) :
UniqueNetId(InSteamId.ConvertToUint64())
{
}
/**
* Constructs this object with the specified net id
*
* @param String textual representation of an id
*/
explicit FUniqueNetIdSteam2(const FString& Str) :
UniqueNetId(FCString::Atoi64(*Str))
{
}
/**
* Constructs this object with the specified net id
*
* @param InUniqueNetId the id to set ours to (assumed to be FUniqueNetIdSteam in fact)
*/
explicit FUniqueNetIdSteam2(const FUniqueNetId& InUniqueNetId) :
UniqueNetId(*(uint64*)InUniqueNetId.GetBytes())
{
}
virtual FName GetType() const override
{
return STEAM_SUBSYSTEM;
}
/**
* Get the raw byte representation of this net id
* This data is platform dependent and shouldn't be manipulated directly
*
* @return byte array of size GetSize()
*/
virtual const uint8* GetBytes() const override
{
return (uint8*)&UniqueNetId;
}
/**
* Get the size of the id
*
* @return size in bytes of the id representation
*/
virtual int32 GetSize() const override
{
return sizeof(uint64);
}
/**
* Check the validity of the id
*
* @return true if this is a well formed ID, false otherwise
*/
virtual bool IsValid() const override
{
return UniqueNetId != 0 && CSteamID(UniqueNetId).IsValid();
}
/**
* Platform specific conversion to string representation of data
*
* @return data in string form
*/
virtual FString ToString() const override
{
return FString::Printf(TEXT("%llu"), UniqueNetId);
}
/**
* Get a human readable representation of the net id
* Shouldn't be used for anything other than logging/debugging
*
* @return id in string form
*/
virtual FString ToDebugString() const override
{
CSteamID SteamID(UniqueNetId);
if (SteamID.IsLobby())
{
return FString::Printf(TEXT("Lobby [0x%llX]"), UniqueNetId);
}
else if (SteamID.BAnonGameServerAccount())
{
return FString::Printf(TEXT("Server [0x%llX]"), UniqueNetId);
}
else if (SteamID.IsValid())
{
const FString NickName(SteamFriends() ? UTF8_TO_TCHAR(SteamFriends()->GetFriendPersonaName(UniqueNetId)) : TEXT("UNKNOWN"));
return FString::Printf(TEXT("%s [0x%llX]"), *NickName, UniqueNetId);
}
else
{
return FString::Printf(TEXT("INVALID [0x%llX]"), UniqueNetId);
}
}
virtual uint32 GetTypeHash() const override
{
return ::GetTypeHash(UniqueNetId);
}
/** Convenience cast to CSteamID */
operator CSteamID()
{
return UniqueNetId;
}
/** Convenience cast to CSteamID */
operator const CSteamID() const
{
return UniqueNetId;
}
/** Convenience cast to CSteamID pointer */
operator CSteamID*()
{
return (CSteamID*)&UniqueNetId;
}
/** Convenience cast to CSteamID pointer */
operator const CSteamID*() const
{
return (const CSteamID*)&UniqueNetId;
}
friend FArchive& operator<<(FArchive& Ar, FUniqueNetIdSteam2& UserId)
{
return Ar << UserId.UniqueNetId;
}
};
#endif
#include "AdvancedSteamFriendsLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedSteamFriendsLog, Log, All);
UENUM(Blueprintable)
enum class SteamAvatarSize : uint8
{
SteamAvatar_INVALID = 0,
SteamAvatar_Small = 1,
SteamAvatar_Medium = 2,
SteamAvatar_Large = 3
};
UENUM(Blueprintable)
enum class ESteamUserOverlayType : uint8
{
/*Opens the overlay web browser to the specified user or groups profile.*/
steamid,
/*Opens a chat window to the specified user, or joins the group chat.*/
chat,
/*Opens a window to a Steam Trading session that was started with the ISteamEconomy / StartTrade Web API.*/
jointrade,
/*Opens the overlay web browser to the specified user's stats.*/
stats,
/*Opens the overlay web browser to the specified user's achievements.*/
achievements,
/*Opens the overlay in minimal mode prompting the user to add the target user as a friend.*/
friendadd,
/*Opens the overlay in minimal mode prompting the user to remove the target friend.*/
friendremove,
/*Opens the overlay in minimal mode prompting the user to accept an incoming friend invite.*/
friendrequestaccept,
/*Opens the overlay in minimal mode prompting the user to ignore an incoming friend invite.*/
friendrequestignore,
/*Opens the invite overlay, invitations sent from this dialog will be for the provided lobby*/
invitetolobby
};
static FString EnumToString(const FString& enumName, uint8 value)
{
const UEnum* EnumPtr = FindFirstObject<UEnum>(*enumName, EFindFirstObjectOptions::None, ELogVerbosity::Warning, TEXT("EumtoString"));
if (!EnumPtr)
return FString();
FString EnumName = EnumPtr->GetNameStringByIndex(value);
return EnumName;
}
USTRUCT(BlueprintType, Category = "Online|SteamAPI|SteamGroups")
struct FBPSteamGroupInfo
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
FBPUniqueNetId GroupID; // Uint64 representation
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
FString GroupName;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
FString GroupTag;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
int32 numOnline = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
int32 numInGame = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
int32 numChatting = 0;
};
UENUM(Blueprintable)
enum class EBPTextFilteringContext : uint8
{
/*Unknown context.*/
FContext_Unknown = 0,
/*Game content, only legally required filtering is performed.*/
FContext_GameContent = 1,
/*Char from another player.*/
FContext_Chat = 2,
/*Character or item name.*/
FContext_Name = 3
};
UCLASS()
class UAdvancedSteamFriendsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Friend List Functions *************//
// Get a texture of a valid friends avatar, STEAM ONLY, Returns invalid texture if the subsystem hasn't loaded that size of avatar yet
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI", meta = (ExpandEnumAsExecs = "Result"))
static UTexture2D * GetSteamFriendAvatar(const FBPUniqueNetId UniqueNetId, EBlueprintAsyncResultSwitch &Result, SteamAvatarSize AvatarSize = SteamAvatarSize::SteamAvatar_Medium);
// Preloads the avatar and name of a steam friend, return whether it is already available or not, STEAM ONLY, Takes time to actually load everything after this is called.
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
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", 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")
static bool IsOverlayEnabled();
// Gets the level of a friends steam account, STEAM ONLY, Returns -1 if the steam level is not known, might need RequestSteamFriendInfo called first.
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
static int32 GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId);
// Gets the persona name of a steam ID, STEAM ONLY, Returns empty if no result, might need RequestSteamFriendInfo called first.
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
static FString GetSteamPersonaName(const FBPUniqueNetId UniqueNetId);
// Creates a unique steam id directly from a string holding a uint64 value, useful for testing
UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")
static FBPUniqueNetId CreateSteamIDFromString(const FString SteamID64);
// Retreives the local steam ID from steam
UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")
static FBPUniqueNetId GetLocalSteamIDFromSteam();
/* Gets the current game played by a friend - AppID is int32 even though steam ids are uint32, can't be helped in blueprint currently
* can use the AppID with the WebAPI GetAppList request.
*/
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI", meta = (ExpandEnumAsExecs = "Result"))
static void GetSteamFriendGamePlayed(const FBPUniqueNetId UniqueNetId, EBlueprintResultSwitch &Result/*, FString & GameName*/, int32 & AppID);
// Get a full list of steam groups
UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|SteamGroups")
static void GetSteamGroups(TArray<FBPSteamGroupInfo> & SteamGroups);
// Initializes text filtering (pre-loading dictonaries)
// Returns if it succeeded, false if filtering is unavailable for the games language
UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|TextFiltering")
static bool InitTextFiltering();
// Attempts to filter a string with the given filtering context
// Returns true if the text has been filtered, false if it hasn't (no filtering required or operation failed)
// If false it will still output the original text
// Textsource is the steam id that is the source of the text (player name / chat)
// Requires that InitTextFiltering be called first!!
UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|TextFiltering")
static bool FilterText(FString TextToFilter, EBPTextFilteringContext Context, const FBPUniqueNetId TextSourceID, FString& FilteredText);
// Returns if steam is running in big picture mode
UFUNCTION(BlueprintPure, Category = "Online|SteamAPI")
static bool IsSteamInBigPictureMode();
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class AdvancedSteamSessions : public IModuleInterface
{
public:
/** IModuleInterface implementation */
void StartupModule();
void ShutdownModule();
};

View File

@@ -0,0 +1,351 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Online.h"
#include "OnlineSubsystem.h"
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#include "steam/isteamugc.h"
#include "steam/isteamremotestorage.h"
#endif
#include "Interfaces/OnlineSessionInterface.h"
// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
// disable the warnings locally. Remove when this is fixed in the SDK
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
// #TODO check back on this at some point
#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
#endif
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#pragma push_macro("ARRAY_COUNT")
#undef ARRAY_COUNT
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(push))
MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
#endif // USING_CODE_ANALYSIS
#include <steam/steam_api.h>
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(pop))
#endif // USING_CODE_ANALYSIS
#pragma pop_macro("ARRAY_COUNT")
#endif
// @todo Steam: See above
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "AdvancedSteamWorkshopLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(AdvancedSteamWorkshopLog, Log, All);
// Using a custom struct because uint32 isn't blueprint supported and I don't want to cast to int32
// due to the size of the workshop it could end up overflowing?
USTRUCT(BlueprintType)
struct FBPSteamWorkshopID
{
GENERATED_USTRUCT_BODY()
public:
uint64 SteamWorkshopID;
FBPSteamWorkshopID()
{
}
FBPSteamWorkshopID(uint64 ID)
{
SteamWorkshopID = ID;
}
};
// General result codes - Copying steams version over
// Check these to future proof
UENUM(BlueprintType)
enum class FBPSteamResult : uint8
{
K_EResultInvalid = 0,
k_EResultOK = 1, // success
k_EResultFail = 2, // generic failure
k_EResultNoConnection = 3, // no/failed network connection
// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
k_EResultInvalidPassword = 5, // password/ticket is invalid
k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
k_EResultInvalidParam = 8, // a parameter is incorrect
k_EResultFileNotFound = 9, // file was not found
k_EResultBusy = 10, // called method busy - action not taken
k_EResultInvalidState = 11, // called object was in an invalid state
k_EResultInvalidName = 12, // name is invalid
k_EResultInvalidEmail = 13, // email is invalid
k_EResultDuplicateName = 14, // name is not unique
k_EResultAccessDenied = 15, // access is denied
k_EResultTimeout = 16, // operation timed out
k_EResultBanned = 17, // VAC2 banned
k_EResultAccountNotFound = 18, // account not found
k_EResultInvalidSteamID = 19, // steamID is invalid
k_EResultServiceUnavailable = 20, // The requested service is currently unavailable
k_EResultNotLoggedOn = 21, // The user is not logged on
k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party)
k_EResultEncryptionFailure = 23, // Encryption or Decryption failed
k_EResultInsufficientPrivilege = 24, // Insufficient privilege
k_EResultLimitExceeded = 25, // Too much of a good thing
k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes)
k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired
k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
k_EResultIPNotFound = 31, // IP address not found
k_EResultPersistFailed = 32, // failed to write change to the data store
k_EResultLockingFailed = 33, // failed to acquire access lock for this operation
k_EResultLogonSessionReplaced = 34,
k_EResultConnectFailed = 35,
k_EResultHandshakeFailed = 36,
k_EResultIOFailure = 37,
k_EResultRemoteDisconnect = 38,
k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested
k_EResultBlocked = 40, // a user didn't allow it
k_EResultIgnored = 41, // target is ignoring sender
k_EResultNoMatch = 42, // nothing matching the request found
k_EResultAccountDisabled = 43,
k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
k_EResultSuspended = 51, // Long running operation (content download) suspended/paused
k_EResultCancelled = 52, // Operation canceled (typically by user: content download)
k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
k_EResultDiskFull = 54, // Operation canceled - not enough disk space.
k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side
k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid
k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
k_EResultIllegalPassword = 61, // The requested new password is not legal
k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
k_EResultHardwareNotCapableOfIPT = 67, //
k_EResultIPTInitError = 68, //
k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
k_EResultFacebookQueryError = 70, // Facebook query returned an error
k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired
k_EResultIPLoginRestrictionFailed = 72,
k_EResultAccountLockedDown = 73,
k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
k_EResultNoMatchingURL = 75,
k_EResultBadResponse = 76, // parse failure, missing field, etc.
k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range
k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen
k_EResultDisabled = 80, // The requested service has been configured to be unavailable
k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid !
k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action
k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted
k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent
k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login
k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted
k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker
k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch
k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match
k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners
k_EResultNotModified = 91, // data not modified
};
// Check these to future proof
UENUM(BlueprintType)
enum class FBPWorkshopFileType : uint8
{
k_EWorkshopFileTypeCommunity = 0,
k_EWorkshopFileTypeMicrotransaction = 1,
k_EWorkshopFileTypeCollection = 2,
k_EWorkshopFileTypeArt = 3,
k_EWorkshopFileTypeVideo = 4,
k_EWorkshopFileTypeScreenshot = 5,
k_EWorkshopFileTypeGame = 6,
k_EWorkshopFileTypeSoftware = 7,
k_EWorkshopFileTypeConcept = 8,
k_EWorkshopFileTypeWebGuide = 9,
k_EWorkshopFileTypeIntegratedGuide = 10,
k_EWorkshopFileTypeMerch = 11,
k_EWorkshopFileTypeControllerBinding = 12,
k_EWorkshopFileTypeSteamworksAccessInvite = 13,
k_EWorkshopFileTypeSteamVideo = 14,
// Update k_EWorkshopFileTypeMax if you add values.
k_EWorkshopFileTypeMax = 15
};
// WorkshopItemDetails Struct
USTRUCT(BlueprintType)
struct FBPSteamWorkshopItemDetails
{
GENERATED_USTRUCT_BODY()
public:
FBPSteamWorkshopItemDetails()
{
ResultOfRequest = FBPSteamResult::k_EResultOK;
FileType = FBPWorkshopFileType::k_EWorkshopFileTypeMax;
CreatorAppID = 0;
ConsumerAppID = 0;
VotesUp = 0;
VotesDown = 0;
CalculatedScore = 0.f;
bBanned = false;
bAcceptedForUse = false;
bTagsTruncated = false;
}
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
FBPSteamWorkshopItemDetails(SteamUGCDetails_t &hUGCDetails)
{
ResultOfRequest = (FBPSteamResult)hUGCDetails.m_eResult;
FileType = (FBPWorkshopFileType)hUGCDetails.m_eFileType;
CreatorAppID = (int32)hUGCDetails.m_nCreatorAppID;
ConsumerAppID = (int32)hUGCDetails.m_nConsumerAppID;
Title = FString(hUGCDetails.m_rgchTitle, k_cchPublishedDocumentTitleMax);
Description = FString(hUGCDetails.m_rgchDescription, k_cchPublishedDocumentDescriptionMax);
ItemUrl = FString(hUGCDetails.m_rgchURL, k_cchPublishedFileURLMax);
VotesUp = (int32)hUGCDetails.m_unVotesUp;
VotesDown = (int32)hUGCDetails.m_unVotesDown;
CalculatedScore = hUGCDetails.m_flScore;
bBanned = hUGCDetails.m_bBanned;
bAcceptedForUse = hUGCDetails.m_bAcceptedForUse;
bTagsTruncated = hUGCDetails.m_bTagsTruncated;
CreatorSteamID = FString::Printf(TEXT("%llu"), hUGCDetails.m_ulSteamIDOwner);
}
FBPSteamWorkshopItemDetails(const SteamUGCDetails_t &hUGCDetails)
{
ResultOfRequest = (FBPSteamResult)hUGCDetails.m_eResult;
FileType = (FBPWorkshopFileType)hUGCDetails.m_eFileType;
CreatorAppID = (int32)hUGCDetails.m_nCreatorAppID;
ConsumerAppID = (int32)hUGCDetails.m_nConsumerAppID;
Title = FString(hUGCDetails.m_rgchTitle, k_cchPublishedDocumentTitleMax);
Description = FString(hUGCDetails.m_rgchDescription, k_cchPublishedDocumentDescriptionMax);
ItemUrl = FString(hUGCDetails.m_rgchURL, k_cchPublishedFileURLMax);
VotesUp = (int32)hUGCDetails.m_unVotesUp;
VotesDown = (int32)hUGCDetails.m_unVotesDown;
CalculatedScore = hUGCDetails.m_flScore;
bBanned = hUGCDetails.m_bBanned;
bAcceptedForUse = hUGCDetails.m_bAcceptedForUse;
bTagsTruncated = hUGCDetails.m_bTagsTruncated;
CreatorSteamID = FString::Printf(TEXT("%llu"), hUGCDetails.m_ulSteamIDOwner);
}
#endif
// Result of obtaining the details
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FBPSteamResult ResultOfRequest;
// Type of file
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FBPWorkshopFileType FileType;
// These two are listed as baked to an int, but is stored as a uint, think its safe to keep int
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
int32 CreatorAppID;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
int32 ConsumerAppID;
// Title of item
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FString Title;
// Description of item
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FString Description;
//Url for a video of website
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FString ItemUrl;
// Votes will be unlikely to go above signed limited
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
int32 VotesUp;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
int32 VotesDown;
// Calculated score
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
float CalculatedScore;
// whether the file was banned
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
bool bBanned;
// developer has specifically flagged this item as accepted in the Workshop
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
bool bAcceptedForUse;
// whether the list of tags was too long to be returned in the provided buffer
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
bool bTagsTruncated;
// Steam ID of the user who created this content.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
FString CreatorSteamID;
/*
PublishedFileId_t m_nPublishedFileId;
uint32 m_rtimeCreated; // time when the published file was created
uint32 m_rtimeUpdated; // time when the published file was last updated
uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable)
ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility
char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file
// file/url information
UGCHandle_t m_hFile; // The handle of the primary file
UGCHandle_t m_hPreviewFile; // The handle of the preview file
char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file
int32 m_nFileSize; // Size of the primary file
int32 m_nPreviewFileSize; // Size of the preview file
uint32 m_unNumChildren; // if m_eFileType == k_EWorkshopFileTypeCollection, then this number will be the number of children contained within the collection
*/
};
UCLASS()
class UAdvancedSteamWorkshopLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
//********* Steam Functions *************//
// Returns IDs for subscribed workshop items, TArray length dictates how many
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop")
static TArray<FBPSteamWorkshopID> GetSubscribedWorkshopItems(int32 & NumberOfItems);
UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop")
static void GetNumSubscribedWorkshopItems(int32 & NumberOfItems);
};

View File

@@ -0,0 +1,101 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#include <steam/steam_api.h>
#endif
#include "SteamNotificationsSubsystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSteamOverlayActivated, bool, bOverlayState);
UCLASS()
class ADVANCEDSTEAMSESSIONS_API USteamNotificationsSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
// Event thrown when the steam overlay switches states
UPROPERTY(BlueprintAssignable, Category = "SteamEvents")
FOnSteamOverlayActivated OnSteamOverlayActivated_Bind;
USteamNotificationsSubsystem() : Super()
{
}
class cSteamEventsStore
{
public:
USteamNotificationsSubsystem* ParentSubsystem = nullptr;
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)
{
}*/
#else
//cSteamEventsStore()
//{
//}
#endif
//~cSteamEventsStore(){}
private:
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
//STEAM_CALLBACK(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
STEAM_CALLBACK_MANUAL(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
#endif
};
cSteamEventsStore MyEvents;
/** Implement this for initialization of instances of the system */
virtual void Initialize(FSubsystemCollectionBase& Collection) override
{
MyEvents.Initialize(this);
}
/** Implement this for deinitialization of instances of the system */
virtual void Deinitialize() override
{
MyEvents.UnInitialize(this);
}
};
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
void USteamNotificationsSubsystem::cSteamEventsStore::OnExternalUITriggered(GameOverlayActivated_t* CallbackData)
{
if (ParentSubsystem)
{
ParentSubsystem->OnSteamOverlayActivated_Bind.Broadcast((bool)CallbackData->m_bActive);
}
}
#endif

View File

@@ -0,0 +1,100 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BlueprintDataDefinitions.h"
// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
// disable the warnings locally. Remove when this is fixed in the SDK
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
// #TODO check back on this at some point
#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
#endif
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
//#include "OnlineSubsystemSteam.h"
#pragma push_macro("ARRAY_COUNT")
#undef ARRAY_COUNT
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(push))
MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
#endif // USING_CODE_ANALYSIS
#include <steam/steam_api.h>
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(pop))
#endif // USING_CODE_ANALYSIS
#pragma pop_macro("ARRAY_COUNT")
#endif
// @todo Steam: See above
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "SteamRequestGroupOfficersCallbackProxy.generated.h"
USTRUCT(BlueprintType, Category = "Online|SteamAPI|SteamGroups")
struct FBPSteamGroupOfficer
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
FBPUniqueNetId OfficerUniqueNetID; // Uint64 representation
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
bool bIsOwner = false;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGroupOfficerDetailsDelegate, const TArray<FBPSteamGroupOfficer> &, OfficerList);
UCLASS(MinimalAPI)
class USteamRequestGroupOfficersCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
virtual ~USteamRequestGroupOfficersCallbackProxy();
// Called when there is a successful results return
UPROPERTY(BlueprintAssignable)
FBlueprintGroupOfficerDetailsDelegate OnSuccess;
// Called when there is an unsuccessful results return
UPROPERTY(BlueprintAssignable)
FBlueprintGroupOfficerDetailsDelegate OnFailure;
// Returns a list of steam group officers
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|SteamAPI|SteamGroups")
static USteamRequestGroupOfficersCallbackProxy* GetSteamGroupOfficerList(UObject* WorldContextObject, FBPUniqueNetId GroupUniqueNetID);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
void OnRequestGroupOfficerDetails( ClanOfficerListResponse_t *pResult, bool bIOFailure);
CCallResult<USteamRequestGroupOfficersCallbackProxy, ClanOfficerListResponse_t> m_callResultGroupOfficerRequestDetails;
#endif
private:
FBPUniqueNetId GroupUniqueID;
UObject* WorldContextObject;
};

View File

@@ -0,0 +1,87 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AdvancedSteamWorkshopLibrary.h"
#include "BlueprintDataDefinitions.h"
// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
// disable the warnings locally. Remove when this is fixed in the SDK
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
// #TODO check back on this at some point
#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
#endif
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
//#include "OnlineSubsystemSteam.h"
#pragma push_macro("ARRAY_COUNT")
#undef ARRAY_COUNT
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(push))
MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
#endif // USING_CODE_ANALYSIS
#include <steam/steam_api.h>
#if USING_CODE_ANALYSIS
MSVC_PRAGMA(warning(pop))
#endif // USING_CODE_ANALYSIS
#pragma pop_macro("ARRAY_COUNT")
#endif
// @todo Steam: See above
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "SteamWSRequestUGCDetailsCallbackProxy.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintWorkshopDetailsDelegate, const FBPSteamWorkshopItemDetails&, WorkShopDetails);
UCLASS(MinimalAPI)
class USteamWSRequestUGCDetailsCallbackProxy : public UOnlineBlueprintCallProxyBase
{
GENERATED_UCLASS_BODY()
// Called when there is a successful results return
UPROPERTY(BlueprintAssignable)
FBlueprintWorkshopDetailsDelegate OnSuccess;
// Called when there is an unsuccessful results return
UPROPERTY(BlueprintAssignable)
FBlueprintWorkshopDetailsDelegate OnFailure;
// Ends the current session
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSteamWorkshop")
static USteamWSRequestUGCDetailsCallbackProxy* GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID);
// UOnlineBlueprintCallProxyBase interface
virtual void Activate() override;
// End of UOnlineBlueprintCallProxyBase interface
private:
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
// Internal callback when the operation completes, calls out to the public success/failure callbacks
void OnUGCRequestUGCDetails(SteamUGCQueryCompleted_t *pResult, bool bIOFailure);
CCallResult<USteamWSRequestUGCDetailsCallbackProxy, SteamUGCQueryCompleted_t> m_callResultUGCRequestDetails;
#endif
private:
FBPSteamWorkshopID WorkShopID;
UObject* WorldContextObject;
};

View File

@@ -0,0 +1,454 @@
// 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);
// Clan functions, add in soon
/*int32 UAdvancedSteamFriendsLibrary::GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
return 0;
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
// clan (group) iteration and access functions
//virtual int GetClanCount() = 0;
//virtual CSteamID GetClanByIndex(int iClan) = 0;
//virtual const char *GetClanName(CSteamID steamIDClan) = 0;
//virtual const char *GetClanTag(CSteamID steamIDClan) = 0;
// returns the most recent information we have about what's happening in a clan
//virtual bool GetClanActivityCounts(CSteamID steamIDClan, int *pnOnline, int *pnInGame, int *pnChatting) = 0;
// for clans a user is a member of, they will have reasonably up-to-date information, but for others you'll have to download the info to have the latest
//virtual SteamAPICall_t DownloadClanActivityCounts(ARRAY_COUNT(cClansToRequest) CSteamID *psteamIDClans, int cClansToRequest) = 0;
// requests information about a clan officer list
// when complete, data is returned in ClanOfficerListResponse_t call result
// this makes available the calls below
// you can only ask about clans that a user is a member of
// note that this won't download avatars automatically; if you get an officer,
// and no avatar image is available, call RequestUserInformation( steamID, false ) to download the avatar
//virtual SteamAPICall_t RequestClanOfficerList(CSteamID steamIDClan) = 0;
// returns the steamID of the clan owner
//virtual CSteamID GetClanOwner(CSteamID steamIDClan) = 0;
// returns the number of officers in a clan (including the owner)
//virtual int GetClanOfficerCount(CSteamID steamIDClan) = 0;
// returns the steamID of a clan officer, by index, of range [0,GetClanOfficerCount)
//virtual CSteamID GetClanOfficerByIndex(CSteamID steamIDClan, int iOfficer) = 0;
return SteamFriends()->GetFriendSteamLevel(id);
}
#endif
return 0;
}*/
void UAdvancedSteamFriendsLibrary::GetSteamGroups(TArray<FBPSteamGroupInfo> & SteamGroups)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
int numClans = SteamFriends()->GetClanCount();
for (int i = 0; i < numClans; i++)
{
CSteamID SteamGroupID = SteamFriends()->GetClanByIndex(i);
if(!SteamGroupID.IsValid())
continue;
FBPSteamGroupInfo GroupInfo;
TSharedPtr<const FUniqueNetId> ValueID(new const FUniqueNetIdSteam2(SteamGroupID));
GroupInfo.GroupID.SetUniqueNetId(ValueID);
SteamFriends()->GetClanActivityCounts(SteamGroupID, &GroupInfo.numOnline, &GroupInfo.numInGame, &GroupInfo.numChatting);
GroupInfo.GroupName = FString(UTF8_TO_TCHAR(SteamFriends()->GetClanName(SteamGroupID)));
GroupInfo.GroupTag = FString(UTF8_TO_TCHAR(SteamFriends()->GetClanTag(SteamGroupID)));
SteamGroups.Add(GroupInfo);
}
}
#endif
}
void UAdvancedSteamFriendsLibrary::GetSteamFriendGamePlayed(const FBPUniqueNetId UniqueNetId, EBlueprintResultSwitch &Result/*, FString & GameName*/, int32 & AppID)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamFriendGamePlayed Had a bad UniqueNetId!"));
Result = EBlueprintResultSwitch::OnFailure;
return;
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
FriendGameInfo_t GameInfo;
bool bIsInGame = SteamFriends()->GetFriendGamePlayed(id, &GameInfo);
if (bIsInGame && GameInfo.m_gameID.IsValid())
{
AppID = GameInfo.m_gameID.AppID();
// Forgot this test and left it in, it is incorrect, you would need restricted access
// And it would only find games in the local library anyway
/*char NameBuffer[512];
int Len = SteamAppList()->GetAppName(GameInfo.m_gameID.AppID(), NameBuffer, 512);
if (Len != -1) // Invalid
{
GameName = FString(UTF8_TO_TCHAR(NameBuffer));
}*/
Result = EBlueprintResultSwitch::OnSuccess;
return;
}
}
#endif
Result = EBlueprintResultSwitch::OnFailure;
}
int32 UAdvancedSteamFriendsLibrary::GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
return 0;
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
return SteamFriends()->GetFriendSteamLevel(id);
}
#endif
return 0;
}
FString UAdvancedSteamFriendsLibrary::GetSteamPersonaName(const FBPUniqueNetId UniqueNetId)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamPersonaName Had a bad UniqueNetId!"));
return FString(TEXT(""));
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
const char* PersonaName = SteamFriends()->GetFriendPersonaName(id);
return FString(UTF8_TO_TCHAR(PersonaName));
}
#endif
return FString(TEXT(""));
}
FBPUniqueNetId UAdvancedSteamFriendsLibrary::CreateSteamIDFromString(const FString SteamID64)
{
FBPUniqueNetId netId;
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!(SteamID64.Len() > 0))
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("CreateSteamIDFromString Had a bad UniqueNetId!"));
return netId;
}
if (SteamAPI_Init())
{
// Already does the conversion
TSharedPtr<const FUniqueNetId> ValueID(new const FUniqueNetIdSteam2(SteamID64));
//FCString::Atoi64(*SteamID64));
netId.SetUniqueNetId(ValueID);
return netId;
}
#endif
return netId;
}
FBPUniqueNetId UAdvancedSteamFriendsLibrary::GetLocalSteamIDFromSteam()
{
FBPUniqueNetId netId;
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
TSharedPtr<const FUniqueNetId> SteamID(new const FUniqueNetIdSteam2(SteamUser()->GetSteamID()));
netId.SetUniqueNetId(SteamID);
}
#endif
return netId;
}
bool UAdvancedSteamFriendsLibrary::RequestSteamFriendInfo(const FBPUniqueNetId UniqueNetId, bool bRequireNameOnly)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("RequestSteamFriendInfo Had a bad UniqueNetId!"));
return false;
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
return !SteamFriends()->RequestUserInformation(id, bRequireNameOnly);
}
#endif
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("RequestSteamFriendInfo Couldn't init steamAPI!"));
return false;
}
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)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Had a bad UniqueNetId!"));
return false;
}
if (SteamAPI_Init())
{
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);
}
return true;
}
#endif
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Couldn't init steamAPI!"));
return false;
}
bool UAdvancedSteamFriendsLibrary::IsOverlayEnabled()
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
return SteamUtils()->IsOverlayEnabled();
}
#endif
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Couldn't init steamAPI!"));
return false;
}
UTexture2D * UAdvancedSteamFriendsLibrary::GetSteamFriendAvatar(const FBPUniqueNetId UniqueNetId, EBlueprintAsyncResultSwitch &Result, SteamAvatarSize AvatarSize)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamFriendAvatar Had a bad UniqueNetId!"));
Result = EBlueprintAsyncResultSwitch::OnFailure;
return nullptr;
}
uint32 Width = 0;
uint32 Height = 0;
if (SteamAPI_Init())
{
//Getting the PictureID from the SteamAPI and getting the Size with the ID
//virtual bool RequestUserInformation( CSteamID steamIDUser, bool bRequireNameOnly ) = 0;
uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
int Picture = 0;
switch(AvatarSize)
{
case SteamAvatarSize::SteamAvatar_Small: Picture = SteamFriends()->GetSmallFriendAvatar(id); break;
case SteamAvatarSize::SteamAvatar_Medium: Picture = SteamFriends()->GetMediumFriendAvatar(id); break;
case SteamAvatarSize::SteamAvatar_Large: Picture = SteamFriends()->GetLargeFriendAvatar(id); break;
default: break;
}
if (Picture == -1)
{
Result = EBlueprintAsyncResultSwitch::AsyncLoading;
return NULL;
}
SteamUtils()->GetImageSize(Picture, &Width, &Height);
// STOLEN FROM ANSWERHUB :p, then fixed because answerhub wasn't releasing the memory O.o
// Also fixed image pixel format and switched to a memcpy instead of manual iteration.
// At some point I should probably reply to that answerhub post with these fixes to prevent people killing their games.....
if (Width > 0 && Height > 0)
{
//Creating the buffer "oAvatarRGBA" and then filling it with the RGBA Stream from the Steam Avatar
uint8 *oAvatarRGBA = new uint8[Width * Height * 4];
//Filling the buffer with the RGBA Stream from the Steam Avatar and creating a UTextur2D to parse the RGBA Steam in
SteamUtils()->GetImageRGBA(Picture, (uint8*)oAvatarRGBA, 4 * Height * Width * sizeof(char));
// Removed as I changed the image bit code to be RGB, I think the original author was unaware that there were different pixel formats
/*
//Swap R and B channels because for some reason the games whack
for (uint32 i = 0; i < (Width * Height * 4); i += 4)
{
uint8 Temp = oAvatarRGBA[i + 0];
oAvatarRGBA[i + 0] = oAvatarRGBA[i + 2];
oAvatarRGBA[i + 2] = Temp;
}*/
UTexture2D* Avatar = UTexture2D::CreateTransient(Width, Height, PF_R8G8B8A8);
// Switched to a Memcpy instead of byte by byte transer
if (FTexturePlatformData* PlatformData = Avatar->GetPlatformData())
{
uint8* MipData = (uint8*)PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memcpy(MipData, (void*)oAvatarRGBA, Height * Width * 4);
PlatformData->Mips[0].BulkData.Unlock();
//Setting some Parameters for the Texture and finally returning it
PlatformData->SetNumSlices(1);
Avatar->NeverStream = true;
//Avatar->CompressionSettings = TC_EditorIcon;
}
// Free RGBA buffer regardless of whether it was used or not
delete[] oAvatarRGBA;
Avatar->UpdateResource();
Result = EBlueprintAsyncResultSwitch::OnSuccess;
return Avatar;
}
else
{
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("Bad Height / Width with steam avatar!"));
}
Result = EBlueprintAsyncResultSwitch::OnFailure;
return nullptr;
}
#endif
UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("STEAM Couldn't be verified as initialized"));
Result = EBlueprintAsyncResultSwitch::OnFailure;
return nullptr;
}
bool UAdvancedSteamFriendsLibrary::InitTextFiltering()
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
return SteamUtils()->InitFilterText();
}
#endif
return false;
}
bool UAdvancedSteamFriendsLibrary::FilterText(FString TextToFilter, EBPTextFilteringContext Context, const FBPUniqueNetId TextSourceID, FString& FilteredText)
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
uint32 BufferLen = TextToFilter.Len() + 10; // Docs say 1 byte excess min, going with 10
char* OutText = new char[BufferLen];
uint64 id = 0;
if (TextSourceID.IsValid())
{
id = *((uint64*)TextSourceID.UniqueNetId->GetBytes());
}
int FilterCount = SteamUtils()->FilterText((ETextFilteringContext)Context, id, TCHAR_TO_ANSI(*TextToFilter), OutText, BufferLen);
if (FilterCount > 0)
{
FilteredText = FString(UTF8_TO_TCHAR(OutText));
delete[] OutText;
return true;
}
delete[] OutText;
}
#endif
FilteredText = TextToFilter;
return false;
}
bool UAdvancedSteamFriendsLibrary::IsSteamInBigPictureMode()
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
return SteamUtils()->IsSteamInBigPictureMode();
}
#endif
return false;
}

View File

@@ -0,0 +1,12 @@
//#include "StandAlonePrivatePCH.h"
#include "AdvancedSteamSessions.h"
void AdvancedSteamSessions::StartupModule()
{
}
void AdvancedSteamSessions::ShutdownModule()
{
}
IMPLEMENT_MODULE(AdvancedSteamSessions, AdvancedSteamSessions)

View File

@@ -0,0 +1,69 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AdvancedSteamWorkshopLibrary.h"
#include "OnlineSubSystemHeader.h"
//General Log
DEFINE_LOG_CATEGORY(AdvancedSteamWorkshopLog);
void UAdvancedSteamWorkshopLibrary::GetNumSubscribedWorkshopItems(int32 & NumberOfItems)
{
NumberOfItems = 0;
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
NumberOfItems = SteamUGC()->GetNumSubscribedItems();
return;
}
else
{
UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : SteamAPI is not Inited!"));
return;
}
#else
UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : Called on an incompatible platform"));
return;
#endif
}
TArray<FBPSteamWorkshopID> UAdvancedSteamWorkshopLibrary::GetSubscribedWorkshopItems(int32 & NumberOfItems)
{
TArray<FBPSteamWorkshopID> outArray;
NumberOfItems = 0;
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
uint32 NumItems = SteamUGC()->GetNumSubscribedItems();
if (NumItems == 0)
return outArray;
// Not using the actual variable above in case someone somehow goes past int32 limits
// Don't want to go negative on the iteration.
NumberOfItems = NumItems;
PublishedFileId_t *fileIds = new PublishedFileId_t[NumItems];
uint32 subItems = SteamUGC()->GetSubscribedItems(fileIds, NumItems);
for (uint32 i = 0; i < subItems; ++i)
{
outArray.Add(FBPSteamWorkshopID(fileIds[i]));
}
delete[] fileIds;
return outArray;
}
else
{
UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : SteamAPI is not Inited!"));
return outArray;
}
#else
UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : Called on an incompatible platform"));
return outArray;
#endif
}

View File

@@ -0,0 +1,121 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "SteamRequestGroupOfficersCallbackProxy.h"
#include "Online/CoreOnline.h"
#include "AdvancedSteamFriendsLibrary.h"
#include "OnlineSubSystemHeader.h"
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#include "steam/isteamfriends.h"
#endif
//#include "OnlineSubsystemSteamTypes.h"
//////////////////////////////////////////////////////////////////////////
// UEndSessionCallbackProxy
USteamRequestGroupOfficersCallbackProxy::USteamRequestGroupOfficersCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
USteamRequestGroupOfficersCallbackProxy::~USteamRequestGroupOfficersCallbackProxy()
{
}
USteamRequestGroupOfficersCallbackProxy* USteamRequestGroupOfficersCallbackProxy::GetSteamGroupOfficerList(UObject* WorldContextObject, FBPUniqueNetId GroupUniqueNetID)
{
USteamRequestGroupOfficersCallbackProxy* Proxy = NewObject<USteamRequestGroupOfficersCallbackProxy>();
Proxy->GroupUniqueID = GroupUniqueNetID;
return Proxy;
}
void USteamRequestGroupOfficersCallbackProxy::Activate()
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
uint64 id = *((uint64*)GroupUniqueID.UniqueNetId->GetBytes());
SteamAPICall_t hSteamAPICall = SteamFriends()->RequestClanOfficerList(id);
m_callResultGroupOfficerRequestDetails.Set(hSteamAPICall, this, &USteamRequestGroupOfficersCallbackProxy::OnRequestGroupOfficerDetails);
return;
}
#endif
TArray<FBPSteamGroupOfficer> EmptyArray;
OnFailure.Broadcast(EmptyArray);
}
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
void USteamRequestGroupOfficersCallbackProxy::OnRequestGroupOfficerDetails(ClanOfficerListResponse_t *pResult, bool bIOFailure)
{
TArray<FBPSteamGroupOfficer> OfficerArray;
//FOnlineSubsystemSteam* SteamSubsystem = (FOnlineSubsystemSteam*)(IOnlineSubsystem::Get(STEAM_SUBSYSTEM));
if (bIOFailure || !pResult || !pResult->m_bSuccess)
{
//if (SteamSubsystem != nullptr)
{
// SteamSubsystem->ExecuteNextTick([this]()
//{
TArray<FBPSteamGroupOfficer> FailureArray;
OnFailure.Broadcast(FailureArray);
//});
}
//OnFailure.Broadcast(OfficerArray);
return;
}
if (SteamAPI_Init())
{
uint64 id = *((uint64*)GroupUniqueID.UniqueNetId->GetBytes());
FBPSteamGroupOfficer Officer;
CSteamID ClanOwner = SteamFriends()->GetClanOwner(id);
Officer.bIsOwner = true;
TSharedPtr<const FUniqueNetId> ValueID(new const FUniqueNetIdSteam2(ClanOwner));
Officer.OfficerUniqueNetID.SetUniqueNetId(ValueID);
OfficerArray.Add(Officer);
for (int i = 0; i < pResult->m_cOfficers; i++)
{
CSteamID OfficerSteamID = SteamFriends()->GetClanOfficerByIndex(id, i);
Officer.bIsOwner = false;
TSharedPtr<const FUniqueNetId> newValueID(new const FUniqueNetIdSteam2(OfficerSteamID));
Officer.OfficerUniqueNetID.SetUniqueNetId(newValueID);
OfficerArray.Add(Officer);
}
//if (SteamSubsystem != nullptr)
//{
//SteamSubsystem->ExecuteNextTick([OfficerArray, this]()
//{
OnSuccess.Broadcast(OfficerArray);
//});
//}
//OnSuccess.Broadcast(OfficerArray);
return;
}
else
{
//if (SteamSubsystem != nullptr)
{
//SteamSubsystem->ExecuteNextTick([this]()
//{
TArray<FBPSteamGroupOfficer> FailureArray;
OnFailure.Broadcast(FailureArray);
//});
}
}
// Should never hit this anyway
//OnFailure.Broadcast(OfficerArray);
}
#endif

View File

@@ -0,0 +1,101 @@
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "SteamWSRequestUGCDetailsCallbackProxy.h"
#include "OnlineSubSystemHeader.h"
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
#include "steam/isteamugc.h"
#endif
//////////////////////////////////////////////////////////////////////////
// UEndSessionCallbackProxy
USteamWSRequestUGCDetailsCallbackProxy::USteamWSRequestUGCDetailsCallbackProxy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
USteamWSRequestUGCDetailsCallbackProxy* USteamWSRequestUGCDetailsCallbackProxy::GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID/*, int32 NumSecondsBeforeTimeout*/)
{
USteamWSRequestUGCDetailsCallbackProxy* Proxy = NewObject<USteamWSRequestUGCDetailsCallbackProxy>();
Proxy->WorkShopID = WorkShopID;
return Proxy;
}
void USteamWSRequestUGCDetailsCallbackProxy::Activate()
{
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
if (SteamAPI_Init())
{
// #TODO: Support arrays instead in the future?
UGCQueryHandle_t hQueryHandle = SteamUGC()->CreateQueryUGCDetailsRequest((PublishedFileId_t *)&WorkShopID.SteamWorkshopID, 1);
// #TODO: add search settings here by calling into the handle?
SteamAPICall_t hSteamAPICall = SteamUGC()->SendQueryUGCRequest(hQueryHandle);
// Need to release the query
SteamUGC()->ReleaseQueryUGCRequest(hQueryHandle);
if (hSteamAPICall == k_uAPICallInvalid)
{
OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
return;
}
m_callResultUGCRequestDetails.Set(hSteamAPICall, this, &USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails);
return;
}
#endif
OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
}
#if (PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX) && STEAM_SDK_INSTALLED
void USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails(SteamUGCQueryCompleted_t *pResult, bool bIOFailure)
{
//FOnlineSubsystemSteam* SteamSubsystem = (FOnlineSubsystemSteam*)(IOnlineSubsystem::Get(STEAM_SUBSYSTEM));
if (bIOFailure || !pResult || pResult->m_unNumResultsReturned <= 0)
{
//if (SteamSubsystem != nullptr)
{
// SteamSubsystem->ExecuteNextTick([this]()
//{
OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
//});
}
//OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
return;
}
if (SteamAPI_Init())
{
SteamUGCDetails_t Details;
if (SteamUGC()->GetQueryUGCResult(pResult->m_handle, 0, &Details))
{
//if (SteamSubsystem != nullptr)
{
//SteamSubsystem->ExecuteNextTick([Details, this]()
//{
OnSuccess.Broadcast(FBPSteamWorkshopItemDetails(Details));
//});
}
//OnSuccess.Broadcast(FBPSteamWorkshopItemDetails(Details));
return;
}
}
else
{
//if (SteamSubsystem != nullptr)
{
//SteamSubsystem->ExecuteNextTick([this]()
//{
OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
//});
}
}
// Not needed, should never hit here
//OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
}
#endif

View File

@@ -0,0 +1,19 @@
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

@@ -0,0 +1,7 @@
### 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

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

10
Plugins/VRExpansionPlugin/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,19 @@
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

@@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

View File

@@ -0,0 +1,53 @@
{
"FileVersion": 3,
"Version": 5.7,
"VersionName": "5.7",
"FriendlyName": "OpenXRExpansionPlugin",
"Description": "An set of utility functions for OpenXR",
"Category": "Virtual Reality",
"CreatedBy": "Joshua (MordenTral) Statzer",
"CreatedByURL": "http://www.vreue4.com",
"DocsURL": "http://www.vreue4.com",
"MarketplaceURL": "",
"SupportURL": "http://www.vreue4.com",
"EnabledByDefault": false,
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": true,
"SupportedTargetPlatforms": [
"Win64",
"Linux",
"Android",
"Mac",
"IOS"
],
"Modules": [
{
"Name": "OpenXRExpansionPlugin",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
,
{
"Name": "OpenXRExpansionEditor",
"Type": "UnCookedOnly",
"LoadingPhase": "PreDefault"
}
],
"Plugins": [
{
"Name": "OpenXR",
"Enabled": true,
"PlatformAllowList": [
"Win64",
"Linux",
"Android"
]
},
{
"Name": "XRBase",
"Enabled": true
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,61 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionEditor : ModuleRules
{
public OpenXRExpansionEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
// ... add other public dependencies that you statically link with here ...
"Engine",
"Core",
"CoreUObject"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"BlueprintGraph",
"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"Slate",
"InputCore",
"Engine",
"EditorStyle",
"AssetRegistry",
"OpenXRExpansionPlugin"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
}

View File

@@ -0,0 +1,34 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimGraphNode_ApplyOpenXRHandPose)
/////////////////////////////////////////////////////
// UAnimGraphNode_ModifyBSHand
UAnimGraphNode_ApplyOpenXRHandPose::UAnimGraphNode_ApplyOpenXRHandPose(const FObjectInitializer& Initializer)
: Super(Initializer)
{
}
//Title Color!
FLinearColor UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitleColor() const
{
return FLinearColor(12, 12, 0, 1);
}
//Node Category
FString UAnimGraphNode_ApplyOpenXRHandPose::GetNodeCategory() const
{
return FString("OpenXR");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetControllerDescription() const
{
return FText::FromString("Apply OpenXR Hand Pose");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FText Result = GetControllerDescription();
return Result;
}

View File

@@ -0,0 +1,16 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
IMPLEMENT_MODULE(FOpenXRExpansionEditorModule, OpenXRExpansionEditor);
void FOpenXRExpansionEditorModule::StartupModule()
{
}
void FOpenXRExpansionEditorModule::ShutdownModule()
{
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include "AnimGraphDefinitions.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Editor/AnimGraph/Public/AnimGraphNode_SkeletalControlBase.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.generated.h"
UCLASS(MinimalAPI)
class UAnimGraphNode_ApplyOpenXRHandPose : public UAnimGraphNode_SkeletalControlBase
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_ApplyOpenXRHandPose Node;
public:
// UEdGraphNode interface
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FLinearColor GetNodeTitleColor() const override;
virtual FString GetNodeCategory() const override;
// End of UEdGraphNode interface
protected:
// UAnimGraphNode_SkeletalControlBase protected interface
virtual FText GetControllerDescription() const;
virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
// End of UAnimGraphNode_SkeletalControlBase protected interface
};

View File

@@ -0,0 +1,13 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Runtime/Core/Public/Modules/ModuleInterface.h"
class FOpenXRExpansionEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@@ -0,0 +1,76 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionPlugin: ModuleRules
{
public OpenXRExpansionPlugin(ReadOnlyTargetRules Target)
: base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
SetupIrisSupport(Target);
PublicDependencyModuleNames.AddRange(
new string[]
{
//"InputDevice",
//"LiveLink",
//"LiveLinkInterface"
}
);
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
PrivateIncludePaths.AddRange(
new string[] {
EngineDir + "Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private",
EngineDir + "/Source/ThirdParty/OpenXR/include",
// ... add other private include paths required here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"NetCore",
"CoreUObject",
//"ApplicationCore",
"Engine",
//"InputDevice",
"InputCore",
"Slate",
"HeadMountedDisplay",
//"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"XRBase"
//"LiveLink",
//"LiveLinkInterface",
}
);
if (Target.Platform != UnrealTargetPlatform.Mac && Target.Platform != UnrealTargetPlatform.IOS)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"OpenXRHMD"
}
);
PrivateDefinitions.AddRange(new string[] { "OPENXR_SUPPORTED" });
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR");
}
// if (Target.bBuildEditor == true)
// {
// PrivateDependencyModuleNames.Add("UnrealEd");
// }
}
}
}

View File

@@ -0,0 +1,488 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AnimNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_ApplyOpenXRHandPose)
//#include "EngineMinimal.h"
//#include "Engine/Engine.h"
//#include "CoreMinimal.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
#include "AnimationRuntime.h"
#include "DrawDebugHelpers.h"
#include "OpenXRHandPoseComponent.h"
#include "Runtime/Engine/Public/Animation/AnimInstanceProxy.h"
#include "BoneControllers/AnimNode_SkeletalControlBase.h"
FAnimNode_ApplyOpenXRHandPose::FAnimNode_ApplyOpenXRHandPose()
: FAnimNode_SkeletalControlBase()
{
WorldIsGame = false;
Alpha = 1.f;
SkeletonType = EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right;
bIsOpenInputAnimationInstance = false;
bSkipRootBone = false;
bOnlyApplyWristTransform = false;
//WristAdjustment = FQuat::Identity;
}
void FAnimNode_ApplyOpenXRHandPose::OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance)
{
Super::OnInitializeAnimInstance(InProxy, InAnimInstance);
if (const UOpenXRAnimInstance * OpenXRAnimInstance = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
bIsOpenInputAnimationInstance = true;
if (OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData.Num())
{
for (int i = 0; i < OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData.Num(); ++i)
{
EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].TargetHand;
if (OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].bMirrorLeftRight)
{
TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
}
if (TargetHand == MappedBonePairs.TargetHand)
{
bIsMirroringHand = OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].bMirrorLeftRight;
break;
}
}
}
}
}
void FAnimNode_ApplyOpenXRHandPose::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
Super::Initialize_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
Super::CacheBones_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::InitializeBoneReferences(const FBoneContainer& RequiredBones)
{
UObject* OwningAsset = RequiredBones.GetAsset();
if (!OwningAsset)
return;
USkeleton* AssetSkeleton = RequiredBones.GetSkeletonAsset();
if (!AssetSkeleton)
return;
if (!MappedBonePairs.bInitialized || OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton)
{
// Trigger a full re-build if our asset changed
if (MappedBonePairs.bInitialized && (OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
MappedBonePairs.ClearMapping();
}
MappedBonePairs.LastInitializedName = OwningAsset->GetFName();
MappedBonePairs.LastInitializedSkeleton = SkeletonType;
MappedBonePairs.bInitialized = false;
if (AssetSkeleton)
{
// If our bone pairs are empty, then setup our sane defaults
if (!MappedBonePairs.BonePairs.Num())
{
MappedBonePairs.ConstructDefaultMappings(SkeletonType, bSkipRootBone);
}
// Construct a reverse map of our joints
MappedBonePairs.ConstructReverseMapping();
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
for (FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
MappedBonePairs.bInitialized = true;
if (SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left || SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right)
{
// We hard code this for now because I don't like their wrist being a different transform
MappedBonePairs.AdjustmentQuat = FRotator(0.f, 90.f, 180.f).Quaternion(); // Current one is incorrect without wrist
// Maybe do it in relative space?
}
else
{
CalculateSkeletalAdjustment(AssetSkeleton);
}
}
}
}
void FAnimNode_ApplyOpenXRHandPose::CalculateSkeletalAdjustment(USkeleton* AssetSkeleton)
{
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
if (!MappedBonePairs.bInitialized || MappedBonePairs.BonePairs.Num() < 4 || !RefBones.Num())
{
UE_LOG(LogTemp, Error, TEXT("Empty or incorrect mapping or skeleton data when calculating skeletal adjustment!"));
return;
}
FBPOpenXRSkeletalPair KnuckleIndexPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleMiddlePair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleRingPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnucklePinkyPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair WristPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT]];
FVector KnuckleAverage = GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleIndexPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleMiddlePair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleRingPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnucklePinkyPair.ReferenceToConstruct.BoneIndex).GetTranslation();
// Get our average across the knuckles
KnuckleAverage /= 4.f;
// Obtain the UE4 wrist Side & Forward directions from first animation frame and place in cache
FTransform WristTransform_UE = GetRefBoneInCS(RefBones, RefBonesInfo, WristPair.ReferenceToConstruct.BoneIndex);
FVector ToKnuckleAverage_UE = KnuckleAverage - WristTransform_UE.GetTranslation();
ToKnuckleAverage_UE.Normalize();
WristForwardLS_UE = WristTransform_UE.GetRotation().UnrotateVector(ToKnuckleAverage_UE);
SetVectorToMaxElement(WristForwardLS_UE);
WristSideDirectionLS = FVector::CrossProduct(WristForwardLS_UE, FVector::RightVector);
SetVectorToMaxElement(WristSideDirectionLS);
CalculateOpenXRAdjustment();
}
void FAnimNode_ApplyOpenXRHandPose::CalculateOpenXRAdjustment()
{
// Base implementation is like valves
// Forward direction
static FVector OpenXRForwardDirection = FVector(1.0f, 0.f, 0.f);
// Side direction
// Do I need to flip this for left hand?
bool bUseLeftHandOffsets = false;
if ((!bIsMirroringHand && MappedBonePairs.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ||
(bIsMirroringHand && MappedBonePairs.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Right))
{
bUseLeftHandOffsets = true;
}
//static FVector OpenXRSideDirection = FVector(0.f, 1.f, 0.f);
FVector OpenXRSideDirection = bUseLeftHandOffsets ? FVector(0.f, -1.f, 0.f) : FVector(0.f, 1.f, 0.f);
// Align forward vectors, openXR once in engine is X+ forward
FQuat AlignmentRot = FQuat::FindBetweenNormals(WristForwardLS_UE, OpenXRForwardDirection);
// Rotate about the aligned forward direction to make the side directions align
FVector WristSideDirectionMS_UE = AlignmentRot * WristSideDirectionLS;
// Rotate around, should the Side direction flip for openXR if its the left hand?
FQuat TwistRotation = CalcRotationAboutAxis(WristSideDirectionMS_UE, OpenXRSideDirection, OpenXRForwardDirection);
FRotator Difference = (TwistRotation * AlignmentRot).Rotator();
MappedBonePairs.AdjustmentQuat = (TwistRotation * AlignmentRot).GetNormalized();
}
void FAnimNode_ApplyOpenXRHandPose::ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
TArray<FTransform> TempWorldTransforms = WorldTransforms;
// Ensure add trans is normalized
AddTrans.NormalizeRotation();
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
bool bUseAutoCalculatedRetarget = AddTrans.Equals(FTransform::Identity);
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
if (TempWorldTransforms[Index].ContainsNaN() || TempWorldTransforms[Index].Equals(FTransform::Identity))
{
OutTransforms[Index] = FTransform::Identity;
//continue;
}
// Ensure normalization
TempWorldTransforms[Index].NormalizeRotation();
if (bMirrorLeftRight)
{
TempWorldTransforms[Index].Mirror(EAxis::Y, EAxis::Y);
}
if (bUseAutoCalculatedRetarget)
{
TempWorldTransforms[Index].ConcatenateRotation(MappedBonePairs.AdjustmentQuat);
//WorldTransforms[Index].ConcatenateRotation(MappedBonePairs.BonePairs[0].RetargetRot);
}
else
{
TempWorldTransforms[Index].ConcatenateRotation(AddTrans.GetRotation());
}
}
// Make this into a single loop, their structure always has children after parent
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform& BoneTransform = TempWorldTransforms[Index];
//BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
// Thumb keeps the metacarpal intact, we don't skip it
if (bMergeMissingUE4Bones)
{
if (Index != (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT && ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (bMergeMissingUE4Bones && ParentParent == 1) // Wrist
{
ParentTransform = TempWorldTransforms[ParentParent];
}
else
{
ParentTransform = TempWorldTransforms[ParentIndex];
}
//ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
}
void FAnimNode_ApplyOpenXRHandPose::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
{
if (!MappedBonePairs.bInitialized)
return;
const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
UObject* OwningAsset = BoneContainer.GetAsset();
if (!OwningAsset)
return;
// Trigger a full re-build if our asset or target skeleton changed, do it up here before finding the correct hand
if ((OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
InitializeBoneReferences(BoneContainer);
}
/*const */FBPOpenXRActionSkeletalData *StoredActionInfoPtr = nullptr;
if (bIsOpenInputAnimationInstance)
{
/*const*/ FOpenXRAnimInstanceProxy* OpenXRAnimInstance = (FOpenXRAnimInstanceProxy*)Output.AnimInstanceProxy;
if (OpenXRAnimInstance->HandSkeletalActionData.Num())
{
for (int i = 0; i <OpenXRAnimInstance->HandSkeletalActionData.Num(); ++i)
{
EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->HandSkeletalActionData[i].TargetHand;
if (OpenXRAnimInstance->HandSkeletalActionData[i].bMirrorLeftRight)
{
TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
}
if (TargetHand == MappedBonePairs.TargetHand)
{
StoredActionInfoPtr = &OpenXRAnimInstance->HandSkeletalActionData[i];
break;
}
}
}
}
// If we have an empty hand pose but have a passed in custom one then use that
if (StoredActionInfoPtr == nullptr || !StoredActionInfoPtr->SkeletalTransforms.Num())
{
StoredActionInfoPtr = &OptionalStoredActionInfo;
}
if (!StoredActionInfoPtr->bHasValidData)
{
return;
}
//MappedBonePairs.AdjustmentQuat = WristAdjustment;
// Currently not blending correctly
const float BlendWeight = FMath::Clamp<float>(ActualAlpha, 0.f, 1.f);
uint8 BoneTransIndex = 0;
uint8 NumBones = StoredActionInfoPtr ? StoredActionInfoPtr->SkeletalTransforms.Num() : 0;
if (NumBones < 1)
{
// Early out, we don't have a valid data to work with
return;
}
FTransform trans = FTransform::Identity;
OutBoneTransforms.Reserve(MappedBonePairs.BonePairs.Num());
TArray<FBoneTransform> TransBones;
FTransform AdditionTransform = StoredActionInfoPtr->AdditionTransform;
FTransform TempTrans = FTransform::Identity;
FTransform ParentTrans = FTransform::Identity;
FTransform * ParentTransPtr = nullptr;
//AdditionTransform.SetRotation(MappedBonePairs.AdjustmentQuat);
TArray<FTransform> HandTransforms;
ConvertHandTransformsSpace(HandTransforms, StoredActionInfoPtr->SkeletalTransforms, AdditionTransform, StoredActionInfoPtr->bMirrorLeftRight, MappedBonePairs.bMergeMissingBonesUE4);
for (const FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
BoneTransIndex = (int8)BonePair.OpenXRBone;
ParentTrans = FTransform::Identity;
if (bSkipRootBone && BonePair.OpenXRBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
continue;
if (BoneTransIndex >= NumBones || BonePair.ReferenceToConstruct.CachedCompactPoseIndex == INDEX_NONE)
continue;
if (!BonePair.ReferenceToConstruct.IsValidToEvaluate(BoneContainer))
{
continue;
}
trans = Output.Pose.GetComponentSpaceTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
if (BonePair.ParentReference != INDEX_NONE)
{
ParentTrans = Output.Pose.GetComponentSpaceTransform(BonePair.ParentReference);
ParentTrans.SetScale3D(FVector(1.f));
}
EXRHandJointType CurrentBone = (EXRHandJointType)BoneTransIndex;
TempTrans = (HandTransforms[BoneTransIndex]);
//TempTrans.ConcatenateRotation(BonePair.RetargetRot);
/*if (StoredActionInfoPtr->bMirrorHand)
{
FMatrix M = TempTrans.ToMatrixWithScale();
M.Mirror(EAxis::Z, EAxis::X);
M.Mirror(EAxis::X, EAxis::Z);
TempTrans.SetFromMatrix(M);
}*/
TempTrans = TempTrans * ParentTrans;
if (StoredActionInfoPtr->bAllowDeformingMesh || bOnlyApplyWristTransform)
trans.SetTranslation(TempTrans.GetTranslation());
trans.SetRotation(TempTrans.GetRotation());
TransBones.Add(FBoneTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex, trans));
// Need to do it per bone so future bones are correct
// Only if in parent space though, can do it all at the end in component space
if (TransBones.Num())
{
Output.Pose.LocalBlendCSBoneTransforms(TransBones, BlendWeight);
TransBones.Reset();
}
if (bOnlyApplyWristTransform && CurrentBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
{
break; // Early out of the loop, we only wanted to apply the wrist
}
}
}
bool FAnimNode_ApplyOpenXRHandPose::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
{
return(/*MappedBonePairs.bInitialized && */MappedBonePairs.BonePairs.Num() > 0);
}

View File

@@ -0,0 +1,595 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRExpansionFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRExpansionFunctionLibrary)
//#include "EngineMinimal.h"
#include "Engine/Engine.h"
#include <openxr/openxr.h>
#include "CoreMinimal.h"
#include "IXRTrackingSystem.h"
//General Log
DEFINE_LOG_CATEGORY(OpenXRExpansionFunctionLibraryLog);
UOpenXRExpansionFunctionLibrary::UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
//=============================================================================
UOpenXRExpansionFunctionLibrary::~UOpenXRExpansionFunctionLibrary()
{
}
void UOpenXRExpansionFunctionLibrary::GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch& Result)
{
#if defined(OPENXR_SUPPORTED)
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
Result = EBPXRResultSwitch::OnFailed;
if (FOpenXRHMD* pOpenXRHMD = GetOpenXRHMD())
{
XrInstance XRInstance = pOpenXRHMD->GetInstance();
XrSystemId XRSysID = pOpenXRHMD->GetSystem();
if (XRSysID && XRInstance)
{
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
systemProperties.next = nullptr;
if (xrGetSystemProperties(XRInstance, XRSysID, &systemProperties) == XR_SUCCESS)
{
XrSession XRSesh = pOpenXRHMD->GetSession();
if (XRSesh)
{
XrPath myPath;
XrResult PathResult = xrStringToPath(XRInstance, "/user/hand/left", &myPath);
XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
interactionProfile.next = nullptr;
XrResult QueryResult = xrGetCurrentInteractionProfile(XRSesh, myPath, &interactionProfile);
if (QueryResult == XR_SUCCESS)
{
char myPathy[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t outputsize;
xrPathToString(XRInstance, interactionProfile.interactionProfile, XR_MAX_SYSTEM_NAME_SIZE, &outputsize, myPathy);
if (interactionProfile.interactionProfile == XR_NULL_PATH || outputsize < 1)
return;
FString InteractionName(ANSI_TO_TCHAR(myPathy));
if (InteractionName.Len() < 1)
return;
/*
* Interaction profile paths [6.4]
An interaction profile identifies a collection of buttons and
other input sources, and is of the form:
/interaction_profiles/<vendor_name>/<type_name>
Paths supported in the core 1.0 release
/interaction_profiles/khr/simple_control
/interaction_profiles/khr/simple_controller
/interaction_profiles/google/daydream_controller
/interaction_profiles/htc/vive_controller
/interaction_profiles/htc/vive_pro
/interaction_profiles/microsoft/motion_controller
/interaction_profiles/microsoft/xbox_controller
/interaction_profiles/oculus/go_controller
/interaction_profiles/oculus/touch_controller
/interaction_profiles/valve/index_controller
*/
// Not working currently?
/*XrInputSourceLocalizedNameGetInfo InputSourceInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
InputSourceInfo.next = nullptr;
InputSourceInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT;
InputSourceInfo.sourcePath = interactionProfile.interactionProfile;
char buffer[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t usedBufferCount = 0;
if (xrGetInputSourceLocalizedName(XRSesh, &InputSourceInfo, XR_MAX_SYSTEM_NAME_SIZE, &usedBufferCount, (char*)&buffer) == XR_SUCCESS)
{
int g = 0;
}*/
if (InteractionName.Find("touch_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusTouchController;
}
else if (InteractionName.Contains("index_controller", ESearchCase::IgnoreCase))
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ValveIndexController;
}
else if (InteractionName.Find("vive_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveController;
}
else if (InteractionName.Find("vive_pro", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveProController;
}
else if (InteractionName.Find("simple_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_SimpleController;
}
else if (InteractionName.Find("daydream_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_DaydreamController;
}
else if (InteractionName.Find("motion_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftMotionController;
}
else if (InteractionName.Find("xbox_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftXboxController;
}
else if (InteractionName.Find("go_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusGoController;
}
else if (InteractionName.Find("neo3_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_PicoNeo3Controller;
}
else if (InteractionName.Find("mixed_reality_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_WMRController;
}
else
{
UE_LOG(OpenXRExpansionFunctionLibraryLog, Warning, TEXT("UNKNOWN OpenXR Interaction profile detected!!!: %s"), *InteractionName);
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
}
Result = EBPXRResultSwitch::OnSucceeded;
}
TrackingSystemName = FString(ANSI_TO_TCHAR(systemProperties.systemName));// , XR_MAX_SYSTEM_NAME_SIZE);
//VendorID = systemProperties.vendorId;
return;
}
}
}
}
#endif
TrackingSystemName.Empty();
return;
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose)
{
FXRHandTrackingState HandTrackingData;
FXRMotionControllerState MotionControllerData;
if (bGetMockUpPose)
{
// #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::GetHandTrackingState((UObject*)HandPoseComponent, EXRSpaceType::UnrealWorldSpace, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, HandTrackingData);
if (HandTrackingData.bValid)
{
HandPoseContainer.SkeletalTransforms.Empty(HandTrackingData.HandKeyLocations.Num());
FTransform ParentTrans = FTransform::Identity;
// #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*/
{
ParentTrans = FTransform(HandTrackingData.HandKeyRotations[(uint8)EHandKeypoint::Palm], HandTrackingData.HandKeyLocations[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
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(HandTrackingData.HandKeyRotations[i].GetNormalized(), HandTrackingData.HandKeyLocations[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
//if (bGetCurlValues)
{
GetFingerCurlValues(HandPoseContainer.SkeletalTransforms, HandPoseContainer.FingerCurls);
}
HandPoseContainer.bHasValidData = (HandPoseContainer.SkeletalTransforms.Num() == EHandKeypointCount);
return true;
}
HandPoseContainer.bHasValidData = false;
return false;
}
void UOpenXRExpansionFunctionLibrary::GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray)
{
// Fail if the count is too low
if (TransformArray.Num() < EHandKeypointCount)
return;
if (CurlArray.Num() < 5)
{
CurlArray.AddZeroed(5);
}
CurlArray[0] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
CurlArray[1] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
CurlArray[2] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
CurlArray[3] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
CurlArray[4] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl)
{
FXRHandTrackingState HandTrackingData;
UHeadMountedDisplayFunctionLibrary::GetHandTrackingState(WorldContextObject, EXRSpaceType::UnrealWorldSpace, TargetHand, HandTrackingData);
// Fail if the count is too low
if (HandTrackingData.HandKeyLocations.Num() < EHandKeypointCount)
return false;
FTransform ParentTrans = FTransform::Identity;
// #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*/
{
ParentTrans = FTransform(HandTrackingData.HandKeyRotations[(uint8)EHandKeypoint::Palm], HandTrackingData.HandKeyLocations[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
TArray<FTransform> TransformArray;
TransformArray.AddUninitialized(HandTrackingData.HandKeyLocations.Num());
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(HandTrackingData.HandKeyRotations[i].GetNormalized(), HandTrackingData.HandKeyLocations[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
}
ThumbCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
IndexCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
MiddleCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
RingCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
PinkyCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
return true;
}
float UOpenXRExpansionFunctionLibrary::GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone)
{
float Angle1 = 0.0f;
float Angle2 = 0.0f;
float Angle1Curl = 0.0f;
float Angle2Curl = 0.0f;
if (RootBone == EHandKeypoint::ThumbMetacarpal)
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
Prox = FVector::VectorPlaneProject(Prox, FVector::UpVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::UpVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::UpVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 64.0f;
Angle2Curl = (Angle2 - 20.0f) / 42.0f;
}
else
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
// We don't use the Y (splay) value, only X and Z plane
Prox = FVector::VectorPlaneProject(Prox, FVector::RightVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::RightVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::RightVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 60.0f;
Angle2Curl = (Angle2 - 10.0f) / 100.0f;
}
// Can lower number of variables by doing these
float FinalAngleAvg = FMath::Clamp((Angle1Curl + Angle2Curl) / 2.0f, 0.0f, 1.0f);
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("Finger Curl %f"), IndexCurl));
return FinalAngleAvg;
}
void UOpenXRExpansionFunctionLibrary::ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform BoneTransform = WorldTransforms[Index];
BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (ParentParent == 1) // Wrist
{
ParentTransform = WorldTransforms[ParentParent];
}
else
{
ParentTransform = WorldTransforms[ParentIndex];
}
ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
// Check on the easy component space conversion first
{
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
const FTransform& BoneTransform = WorldTransforms[Index];
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex > 0)
{
if (ParentParent == 1)
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentParent];
}
else
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentIndex];
}
}
}
}
}
void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRHandTrackingState& HandTrackingData, FXRMotionControllerState& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
{
TArray<FQuat> HandRotationsClosed = {
// Closed palm
FQuat(-6.9388939039072284e-18f,-2.7755575615628914e-17f,-5.5511151231257827e-17f,1.0000000181623150),
FQuat(0.0010158333104005046f,-0.031842494413126823f,0.0082646248419453450f,-0.99945823120983279),
FQuat(0.49284713488980170f,-0.22607130273287540f,-0.44054329019960731f,0.71548243409346490),
FQuat(-0.60572821120045273f,-0.017497510794223320f,0.10943807503774633f,-0.78791529705201735),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.11514346379358367f,-0.029229397612833233f,-0.076351329575539195f,0.98997885626789228),
FQuat(0.079333339113826437f,-0.72590009974051883f,0.050346047334316274f,-0.68135202233808378),
FQuat(0.0032539550806410245f,-0.97123336736010268f,0.098921247251489333f,0.21658665949113542),
FQuat(-0.064585069112672477f,-0.57963374972897053f,0.075445998290538954f,0.80880246209406348),
FQuat(0.064585069112672477f,0.57963374972897053f,-0.075445998290538954f,-0.80880246209406348),
FQuat(-7.7702472130181111e-08f,9.8656527815210726e-08f,1.3838491007001075e-06f,1.0000000181613493),
FQuat(0.085300231549708214f,-0.74048187833139134f,0.058532016219761618f,-0.66406663653752407),
FQuat(0.011595964719175678f,-0.98786834549923641f,0.099110835214894707f,0.11899052159928070),
FQuat(-0.063530326287074640f,-0.56012988021281451f,0.076145543493668810f,0.82244775363868539),
FQuat(0.030477923994508188f,0.55662337489051250f,-0.098559802475379960f,-0.82433459003921772),
FQuat(-0.025910339872061101f,0.052311401725670628f,0.042953782692297438f,0.99737013210455761),
FQuat(0.11635892750396379f,-0.74717191584145570f,-0.026909776929005647f,-0.65381238012001353),
FQuat(0.098078041656806447f,-0.98532297068866348f,0.071135591008198620f,0.12024601945419003),
FQuat(0.0028091467060491482f,-0.52817558741956572f,0.11864668261714340f,0.84080060571895254),
FQuat(-0.0028091467060491482f,0.52817558741956572f,-0.11864668261714340f,-0.84080060571895254),
FQuat(-0.11913260527111892f,0.10934177100849747f,0.11664955310670821f,0.97992077106196507),
FQuat(-0.18696185363314571f,0.78123174637415782f,0.043590203318890380f,0.59398834520762267),
FQuat(0.15903913486884827f,-0.97287570092949460f,0.091728860868847406f,0.14073122087670015),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579)
};
TArray<FQuat> HandRotationsOpen = {
// Open Hand
FQuat(0.167000905f,-0.670308471f,0.304047525f,-0.656011939f),
FQuat(-0.129862994f,0.736467659f,-0.315623045f,0.584065497f),
FQuat(-0.030090153f,0.121532254f,-0.840178490f,0.527659237f),
FQuat(-0.126470163f,-0.262596279f,0.816956878f,-0.497623593f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.277370781f,0.686735749f,-0.258646101f,0.620130479f),
FQuat(0.193366051f,-0.808131576f,0.260262072f,-0.491728455f),
FQuat(0.145547777f,-0.854364336f,0.317562312f,-0.384749293f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.166999936f,-0.670307159f,0.304047883f,-0.656013489f),
FQuat(0.206125781f,-0.815250278f,0.203173012f,-0.501596987f),
FQuat(0.164493740f,-0.890369833f,0.257293820f,-0.337613612f),
FQuat(0.114019498f,-0.937856555f,0.283619940f,-0.164267495f),
FQuat(0.107336335f,-0.925720870f,0.321023613f,-0.168710276f),
FQuat(0.156629071f,-0.719596088f,0.210793152f,-0.642817795f),
FQuat(0.194258988f,-0.858762920f,0.127883837f,-0.456546605f),
FQuat(0.166189745f,-0.930981100f,0.161785051f,-0.281922638f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.160288095f,-0.812664807f,0.100923792f,-0.551087677f),
FQuat(0.207311243f,-0.870556056f,0.056644741f,-0.442656904f),
FQuat(0.191506147f,-0.944826961f,0.096772499f,-0.247511998f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f)
};
HandTrackingData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
TArray<FVector> HandPositionsClosed = {
// Closed palm - Left
FVector(0.0000000000000000f,0.0000000000000000f,0.0000000000000000f),
FVector(-2.8690212431406792f,0.70708009295073815f,-0.47404338536985718f),
FVector(-1.1322360817697272f,-2.1125772981974671f,-1.2278703475596775f),
FVector(0.92697682070144727f,-5.5601677377459957f,-1.6753327187355360f),
FVector(4.0987554778339428f,-6.0520138168462640f,-2.1960898756852747f),
FVector(5.5854053809842918f,-5.4247506634349065f,-2.6631245417791525f),
FVector(-1.6026417502203387f,-1.4646945203797794f,-0.17057236434820122f),
FVector(5.7352432311721007f,-2.5389617545260998f,0.39061644722637634f),
FVector(5.4801464829170561f,-3.3344912297783416f,-3.8566611419550343f),
FVector(2.9179605371815693f,-3.2311985822561073f,-2.6652727318443148f),
FVector(3.2708935578922342f,-3.0117453368521279f,-1.6311186720587312f),
FVector(-1.1935619377191149f,-1.8034793735494103e-05f,1.4147048846974153e-06f),
FVector(5.9028526610092893f,1.3513666817788206e-05f,-4.3170989212359956e-06f),
FVector(5.4567759872551527f,-0.87968929643487392f,-4.1965100581882382f),
FVector(2.2252652348065158f,-0.87742006725177069f,-3.4067970851427791f),
FVector(2.6916877869696085f,-0.62360084690574125f,-2.2285708116727738f),
FVector(-1.1796822503165614f,1.3653411443775685f,-0.17694011615865479f),
FVector(5.3502831208188670f,1.9121382570769896f,-0.87930289382919313f),
FVector(4.8743654830862742f,1.3526757302541959f,-4.8457258101076217f),
FVector(2.1622015314362244f,0.85068796311660544f,-4.1307752690132205f),
FVector(2.6021369184528194f,1.0596020074600654f,-3.1860412064934174f),
FVector(-1.2905361603753163f,2.6108535683555365f,-0.46423293549223010f),
FVector(4.6820094577722964f,3.8858425146699327f,-1.9880098921962746f),
FVector(4.0115118130532306f,3.1678700881616777f,-4.8092930847360869f),
FVector(2.3757993445967389f,2.6579252395479291f,-4.2645319235961239f),
FVector(2.7329133227289351f,2.8811366857469527f,-3.6179750674182261f)
};
// Open Hand
TArray<FVector> HandPositionsOpen = {
FVector(-1014.001f,-478.278f,212.902f),
FVector(-1013.516f,-476.006f,214.688f),
FVector(-1016.362f,-479.642f,215.119f),
FVector(-1018.145f,-483.254f,214.805f),
FVector(-1019.682f,-485.682f,213.284f),
FVector(-1020.480f,-486.982f,212.581f),
FVector(-1014.360f,-478.927f,215.169f),
FVector(-1014.932f,-484.146f,209.902f),
FVector(-1016.872f,-486.643f,206.852f),
FVector(-1018.771f,-488.058f,205.231f),
FVector(-1019.613f,-488.507f,204.655f),
FVector(-1013.901f,-477.534f,213.831f),
FVector(-1014.494f,-481.954f,208.310f),
FVector(-1016.269f,-484.282f,205.146f),
FVector(-1018.657f,-485.834f,203.427f),
FVector(-1019.846f,-486.231f,203.113f),
FVector(-1013.816f,-476.436f,213.006f),
FVector(-1014.637f,-479.707f,207.344f),
FVector(-1016.703f,-481.540f,204.355f),
FVector(-1018.962f,-482.692f,203.000f),
FVector(-1019.978f,-482.975f,202.870f),
FVector(-1013.845f,-475.325f,212.363f),
FVector(-1015.993f,-477.665f,206.928f),
FVector(-1017.571f,-478.907f,204.670f),
FVector(-1019.033f,-479.652f,203.887f),
FVector(-1019.778f,-479.842f,203.819f)
};
HandTrackingData.HandKeyLocations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
/*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);
}
else
{
MotionControllerData.GripPosition = FVector(-1202.619f, -521.077f, 283.076f);
MotionControllerData.GripRotation = FQuat(0.040843058f, 0.116659224f, 0.980030060f, -0.155767411f);
}*/
HandTrackingData.DeviceName = TEXT("OpenXR");
SkeletalMappingData.SkeletalTransforms.Empty(SkeletalMappingData.SkeletalTransforms.Num());
FTransform ParentTrans = FTransform(MotionControllerData.GripUnrealSpaceRotation, MotionControllerData.GripUnrealSpaceLocation, FVector(1.f));
for (int i = 0; i < HandTrackingData.HandKeyLocations.Num(); i++)
{
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,24 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionPlugin.h"
#include "OpenXRExpansionFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "FXRExpansionPluginModule"
void FOpenXRExpansionPluginModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
//LoadOpenVRModule();
}
void FOpenXRExpansionPluginModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
// UnloadOpenVRModule();
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FOpenXRExpansionPluginModule, OpenXRExpansionPlugin)

View File

@@ -0,0 +1,895 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRHandPoseComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRHandPoseComponent)
#include "Net/UnrealNetwork.h"
#include "MotionControllerComponent.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "Engine/NetSerialization.h"
#include "Net/Core/PushModel/PushModel.h"
#include "XRMotionControllerBase.h" // for GetHandEnumForSourceName()
//#include "EngineMinimal.h"
UOpenXRHandPoseComponent::UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
ReplicationRateForSkeletalAnimations = 10.f;
bReplicateSkeletalData = false;
bSmoothReplicatedSkeletalData = true;
SkeletalNetUpdateCount = 0.f;
bDetectGestures = true;
SetIsReplicatedByDefault(true);
bGetMockUpPoseForDebugging = false;
}
void UOpenXRHandPoseComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams SkipOwnerParams;
SkipOwnerParams.Condition = COND_SkipOwner;
SkipOwnerParams.bIsPushBased = true;
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_WITH_PARAMS_FAST(UOpenXRHandPoseComponent, LeftHandRep, SkipOwnerParams);
DOREPLIFETIME_WITH_PARAMS_FAST(UOpenXRHandPoseComponent, RightHandRep, SkipOwnerParams);
}
void UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Implementation(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == SkeletalInfo.TargetHand)
{
if (SkeletalInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
LeftHandRep = SkeletalInfo;
#if WITH_PUSH_MODEL
MARK_PROPERTY_DIRTY_FROM_NAME(UOpenXRHandPoseComponent, LeftHandRep, this);
#endif
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
else
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
RightHandRep = SkeletalInfo;
#if WITH_PUSH_MODEL
MARK_PROPERTY_DIRTY_FROM_NAME(UOpenXRHandPoseComponent, RightHandRep, this);
#endif
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
break;
}
}
}
bool UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Validate(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
return true;
}
void FOpenXRAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
Super::PreUpdate(InAnimInstance, DeltaSeconds);
if (UOpenXRAnimInstance* OwningInstance = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
if (OwningInstance->OwningPoseComp)
{
if (HandSkeletalActionData.Num() != OwningInstance->OwningPoseComp->HandSkeletalActions.Num())
{
HandSkeletalActionData.Empty(OwningInstance->OwningPoseComp->HandSkeletalActions.Num());
for(FBPOpenXRActionSkeletalData& actionInfo : OwningInstance->OwningPoseComp->HandSkeletalActions)
{
HandSkeletalActionData.Add(actionInfo);
}
}
else
{
for (int i = 0; i < OwningInstance->OwningPoseComp->HandSkeletalActions.Num(); ++i)
{
HandSkeletalActionData[i] = OwningInstance->OwningPoseComp->HandSkeletalActions[i];
}
}
}
}
}
FOpenXRAnimInstanceProxy::FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance)
: FAnimInstanceProxy(InAnimInstance)
{
}
void UOpenXRHandPoseComponent::BeginPlay()
{
/*if (UMotionControllerComponent * MotionParent = Cast<UMotionControllerComponent>(GetAttachParent()))
{
EControllerHand HandType;
if (!FXRMotionControllerBase::GetHandEnumForSourceName(MotionParent->MotionSource, HandType))
{
HandType = EControllerHand::Left;
}
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandType == EControllerHand::Left || HandType == EControllerHand::AnyHand)
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
else
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
}
}*/
Super::BeginPlay();
}
void UOpenXRHandPoseComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (!IsLocallyControlled())
{
if (bReplicateSkeletalData)
{
// Handle bone lerping here if we are replicating
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (bSmoothReplicatedSkeletalData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
LeftHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
else
{
RightHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
}
}
}
}
else // Get data and process
{
bool bGetCompressedTransforms = false;
if (bReplicateSkeletalData && HandSkeletalActions.Num() > 0)
{
SkeletalNetUpdateCount += DeltaTime;
if (SkeletalNetUpdateCount >= (1.0f / ReplicationRateForSkeletalAnimations))
{
SkeletalNetUpdateCount = 0.0f;
bGetCompressedTransforms = true;
}
}
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(actionInfo, this, bGetMockUpPoseForDebugging))
{
if (bGetCompressedTransforms)
{
if (GetNetMode() == NM_Client)
{
if (actionInfo.bHasValidData)
{
FBPXRSkeletalRepContainer ContainerSend;
ContainerSend.CopyForReplication(actionInfo);
Server_SendSkeletalTransforms(ContainerSend);
}
}
else
{
if (actionInfo.bHasValidData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
LeftHandRep.CopyForReplication(actionInfo);
else
RightHandRep.CopyForReplication(actionInfo);
}
}
}
}
if (bDetectGestures && actionInfo.bHasValidData && actionInfo.SkeletalTransforms.Num() > 0 && GesturesDB != nullptr && GesturesDB->Gestures.Num() > 0)
{
DetectCurrentPose(actionInfo);
}
}
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
bool UOpenXRHandPoseComponent::SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave)
{
if (!HandSkeletalActions.Num())
return false;
// Default to the first hand element so that single length arrays work as is.
FBPOpenXRActionSkeletalData* HandSkeletalAction = nullptr;
// Now check for the specific passed in hand if this is a multi hand
for (int i = 0; i < HandSkeletalActions.Num(); ++i)
{
if (HandSkeletalActions[i].TargetHand == HandToSave)
{
HandSkeletalAction = &HandSkeletalActions[i];
break;
}
}
if (!HandSkeletalAction || !HandSkeletalAction->bHasValidData || HandSkeletalAction->SkeletalTransforms.Num() < EHandKeypointCount)
return false;
if (GesturesDB)
{
FOpenXRGesture NewGesture;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
for (int i = 0; i < 5; ++i)
{
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc, (EXRHandJointType)FingerMap[i]);
}
else
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc, (EXRHandJointType)FingerMap[i]);
}
}
NewGesture.Name = RecordingName;
GesturesDB->Gestures.Add(NewGesture);
return true;
}
return false;
}
bool UOpenXRHandPoseComponent::K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1)
return false;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (const FOpenXRGesture& Gesture : GesturesDB->Gestures)
{
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
FVector GestureV = Gesture.FingerValues[i].Value;
FVector CurrentV = CurrentTips[i];
FVector Difference = GestureV - CurrentV;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
GestureOut = Gesture;
return true;
}
}
return false;
}
bool UOpenXRHandPoseComponent::DetectCurrentPose(FBPOpenXRActionSkeletalData &SkeletalAction)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
return false;
FTransform BoneTransform = FTransform::Identity;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (auto GestureIterator = GesturesDB->Gestures.CreateConstIterator(); GestureIterator; ++GestureIterator)
{
const FOpenXRGesture &Gesture = *GestureIterator;
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
if (Gesture.FingerValues[i].Threshold <= 0.0f)
continue;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
if (SkeletalAction.LastHandGesture != Gesture.Name)
{
if (SkeletalAction.LastHandGesture != NAME_None)
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = Gesture.Name;
SkeletalAction.LastHandGestureIndex = GestureIterator.GetIndex();
OnNewGestureDetected.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
return true;
}
else
return false; // Same gesture
}
}
if (SkeletalAction.LastHandGesture != NAME_None)
{
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = NAME_None;
SkeletalAction.LastHandGestureIndex = INDEX_NONE;
}
return false;
}
UOpenXRHandPoseComponent::FTransformLerpManager::FTransformLerpManager()
{
bReplicatedOnce = false;
bLerping = false;
UpdateCount = 0.0f;
UpdateRate = 0.0f;
}
void UOpenXRHandPoseComponent::FTransformLerpManager::PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
if (!bExponentialSmoothing)
{
if (ActionInfo.SkeletalTransforms.Num())
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
UpdateRate = (1.0f / NetUpdateRate);
if (bReplicatedOnce)
{
bLerping = true;
UpdateCount = 0.0f;
NewTransforms = ActionInfo.SkeletalTransforms;
ActionInfo.SkeletalTransforms = OldTransforms;
}
else
{
if (bExponentialSmoothing)
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
bReplicatedOnce = true;
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent* ParentComp)
{
if (!ActionInfo.bHasValidData || !OldTransforms.Num())
return;
if (bLerping)
{
bool bExponentialSmoothing = ParentComp->bUseExponentialSmoothing;
float LerpVal = 0.0f;
if (!bExponentialSmoothing || ParentComp->InterpolationSpeed <= 0.f)
{
UpdateCount += DeltaTime;
// Keep LerpVal
LerpVal = FMath::Clamp(UpdateCount / UpdateRate, 0.0f, 1.0f);
}
else
{
LerpVal = FMath::Clamp(DeltaTime * ParentComp->InterpolationSpeed, 0.f, 1.f);
}
if (!bExponentialSmoothing && LerpVal >= 1.0f)
{
bLerping = false;
UpdateCount = 0.0f;
ActionInfo.SkeletalTransforms = NewTransforms;
}
else
{
int32 BoneCountAdjustment = 6 + (ActionInfo.bEnableUE4HandRepSavings ? 4 : 0);
if ((NewTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment)) || (NewTransforms.Num() != ActionInfo.SkeletalTransforms.Num()))
{
return;
}
ActionInfo.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity;
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_Thumb3, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
// These are copied from the 3rd joints as they use the same transform but a different root
// Don't want to waste cpu time blending these
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_Thumb] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Thumb2];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_IndexFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_IndexFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_MiddleFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_MiddleFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_RingFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_RingFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_PinkyFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_PinkyFinger3];
}
}
}
void FBPXRSkeletalRepContainer::CopyForReplication(FBPOpenXRActionSkeletalData& Other)
{
TargetHand = Other.TargetHand;
if (!Other.bHasValidData)
return;
bAllowDeformingMesh = Other.bAllowDeformingMesh;
bEnableUE4HandRepSavings = Other.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//SkeletalTransforms = Other.SkeletalData.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() < EHandKeypointCount)
{
SkeletalTransforms.Empty();
return;
}
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
if (SkeletalTransforms.Num() != EHandKeypointCount - BoneCountAdjustment)
{
SkeletalTransforms.Reset(EHandKeypointCount - BoneCountAdjustment); // Minus bones we don't need
SkeletalTransforms.AddUninitialized(EHandKeypointCount - BoneCountAdjustment);
}
int32 idx = 0;
// Root is always identity
//SkeletalTransforms[0] = Other.SkeletalData.SkeletalTransforms[(uint8)EVROpenInputBones::eBone_Root]; // This has no pos right? Need to skip pos on it
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT];
}
void FBPXRSkeletalRepContainer::CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other)
{
int32 BoneCountAdjustment = 6 + (Container.bEnableUE4HandRepSavings ? 4 : 0);
if (Container.SkeletalTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment))
{
Other.SkeletalTransforms.Empty();
Other.bHasValidData = false;
return;
}
Other.bAllowDeformingMesh = Container.bAllowDeformingMesh;
Other.bEnableUE4HandRepSavings = Container.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//Other.SkeletalData.SkeletalTransforms = Container.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() != EHandKeypointCount)
{
Other.SkeletalTransforms.Reset(EHandKeypointCount);
Other.SkeletalTransforms.AddUninitialized(EHandKeypointCount);
}
int32 idx = 0;
// Only fill in the ones that we care about
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity; // Always identity
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT] = FTransform::Identity;
Other.bHasValidData = true;
}
bool FBPXRSkeletalRepContainer::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
Ar.SerializeBits(&TargetHand, 1);
Ar.SerializeBits(&bAllowDeformingMesh, 1);
Ar.SerializeBits(&bEnableUE4HandRepSavings, 1);
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
bool bHasValidData = SkeletalTransforms.Num() >= TransformCount;
Ar.SerializeBits(&bHasValidData, 1);
//Ar << TransformCount;
if (Ar.IsLoading())
{
SkeletalTransforms.Reset(TransformCount);
}
FVector Position = FVector::ZeroVector;
FRotator Rot = FRotator::ZeroRotator;
if (bHasValidData)
{
for (int i = 0; i < TransformCount; i++)
{
if (Ar.IsSaving())
{
if (bAllowDeformingMesh)
Position = SkeletalTransforms[i].GetLocation();
Rot = SkeletalTransforms[i].Rotator();
}
if (bAllowDeformingMesh)
bOutSuccess &= SerializePackedVector<10, 11>(Position, Ar);
Rot.SerializeCompressed(Ar); // Short? 10 bit?
if (Ar.IsLoading())
{
if (bAllowDeformingMesh)
SkeletalTransforms.Add(FTransform(Rot, Position));
else
SkeletalTransforms.Add(FTransform(Rot));
}
}
}
return bOutSuccess;
}
void UOpenXRAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}
/*void UOpenXRAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}*/
void UOpenXRAnimInstance::InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData& SkeletalMappingData)
{
USkeleton* AssetSkeleton = this->CurrentSkeleton;//RequiredBones.GetSkeletonAsset();
if (AssetSkeleton)
{
FBoneContainer& RequiredBones = this->GetRequiredBones();
for (FBPOpenXRSkeletalPair& BonePair : SkeletalMappingData.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
if (UObject* OwningAsset = RequiredBones.GetAsset())
{
SkeletalMappingData.LastInitializedName = OwningAsset->GetFName();
}
SkeletalMappingData.bInitialized = true;
return;
}
SkeletalMappingData.bInitialized = false;
}

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) <20> 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) <20> 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

@@ -0,0 +1,121 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Runtime/AnimGraphRuntime/Public/BoneControllers/AnimNode_SkeletalControlBase.h"
#include "OpenXRExpansionTypes.h"
//#include "Skeleton/BodyStateSkeleton.h"
//#include "BodyStateAnimInstance.h"
#include "AnimNode_ApplyOpenXRHandPose.generated.h"
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FAnimNode_ApplyOpenXRHandPose : public FAnimNode_SkeletalControlBase
{
GENERATED_USTRUCT_BODY()
public:
FVector WristForwardLS_UE;
FVector WristSideDirectionLS;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
EVROpenXRSkeletonType SkeletonType;
// If your hand is part of a full body or arm skeleton and you don't have a proxy bone to retain the position enable this
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bSkipRootBone;
// If you only want to use the wrist transform part of this
// This will also automatically add the deform to the wrist as it doesn't make much sense without it
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bOnlyApplyWristTransform;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
FBPOpenXRActionSkeletalData OptionalStoredActionInfo;
// MappedBonePairs, if you leave it blank then they will auto generate based off of the SkeletonType
// Otherwise, fill out yourself.
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinHiddenByDefault))
FBPOpenXRSkeletalMappingData MappedBonePairs;
bool bIsOpenInputAnimationInstance = false;
bool bIsMirroringHand = false;
void ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones);
void CalculateSkeletalAdjustment(USkeleton* AssetSkeleton);
void CalculateOpenXRAdjustment();
FQuat CalcRotationAboutAxis(const FVector& FromDirection, const FVector& ToDirection, const FVector& Axis)
{
FVector FromDirectionCp = FVector::CrossProduct(Axis, FromDirection);
FVector ToDirectionCp = FVector::CrossProduct(Axis, ToDirection);
return FQuat::FindBetweenVectors(FromDirectionCp, ToDirectionCp);
}
FTransform GetRefBoneInCS(TArray<FTransform>& RefBones, TArray<FMeshBoneInfo>& RefBonesInfo, int32 BoneIndex)
{
FTransform BoneTransform;
if (BoneIndex >= 0)
{
BoneTransform = RefBones[BoneIndex];
if (RefBonesInfo[BoneIndex].ParentIndex >= 0)
{
BoneTransform *= GetRefBoneInCS(RefBones, RefBonesInfo, RefBonesInfo[BoneIndex].ParentIndex);
}
}
return BoneTransform;
}
void SetVectorToMaxElement(FVector& vec)
{
FVector absVal = vec.GetAbs();
if (absVal.X > absVal.Y && absVal.X > absVal.Z)
{
vec = vec.GetSignVector() * FVector(1.0f, 0.f, 0.f);
vec.Normalize();
}
else if (absVal.Y > absVal.X && absVal.Y > absVal.Z)
{
vec = vec.GetSignVector() * FVector(0.0f, 1.f, 0.f);
vec.Normalize();
}
else if (absVal.Z > absVal.X && absVal.Z > absVal.Y)
{
vec = vec.GetSignVector() * FVector(0.0f, 0.f, 1.f);
vec.Normalize();
}
else
{
vec.Normalize();
}
}
// FAnimNode_SkeletalControlBase interface
//virtual void UpdateInternal(const FAnimationUpdateContext& Context) override;
virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) override;
virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
// End of FAnimNode_SkeletalControlBase interface
virtual void OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance) override;
virtual bool NeedsOnInitializeAnimInstance() const override { return true; }
virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
// Constructor
FAnimNode_ApplyOpenXRHandPose();
protected:
bool WorldIsGame;
AActor* OwningActor;
private:
};

View File

@@ -0,0 +1,102 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
#if defined(OPENXR_SUPPORTED)
#include "OpenXRCore.h"
#include "OpenXRHMD.h"
#endif
#include "OpenXRExpansionTypes.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "OpenXRExpansionFunctionLibrary.generated.h"
#if !defined(OPENXR_SUPPORTED)
class FOpenXRHMD;
#endif
DECLARE_LOG_CATEGORY_EXTERN(OpenXRExpansionFunctionLibraryLog, Log, All);
// This needs to be updated as the original gets changed, that or hope they make the original blueprint accessible.
UENUM(Blueprintable)
enum class EBPOpenXRControllerDeviceType : uint8
{
DT_SimpleController,
DT_ValveIndexController,
DT_ViveController,
DT_ViveProController,
//DT_CosmosController,
DT_DaydreamController,
DT_OculusTouchController,
DT_OculusGoController,
DT_MicrosoftMotionController,
DT_MicrosoftXboxController,
DT_PicoNeo3Controller,
DT_WMRController,
DT_UnknownController
};
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRExpansionFunctionLibrary : public UBlueprintFunctionLibrary
{
//GENERATED_BODY()
GENERATED_BODY()
public:
UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer);
~UOpenXRExpansionFunctionLibrary();
public:
static FOpenXRHMD* GetOpenXRHMD()
{
#if defined(OPENXR_SUPPORTED)
static FName SystemName(TEXT("OpenXR"));
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
return static_cast<FOpenXRHMD*>(GEngine->XRSystem.Get());
}
#endif
return nullptr;
}
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static bool GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose = false);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray);
// Get the estimated curl values from hand tracking
// Will return true if it was able to get the curls, false if it could not (hand tracking not enabled or no data for the tracked index)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (WorldContext = "WorldContextObject"))
static bool GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl);
static float GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms);
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
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)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true", ExpandEnumAsExecs = "Result"))
static void GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch &Result);
};

View File

@@ -0,0 +1,18 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FOpenXRExpansionPluginModule : public IModuleInterface
{
public:
FOpenXRExpansionPluginModule()
{
}
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@@ -0,0 +1,486 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#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"
#include "OpenXRExpansionTypes.generated.h"
// This makes a lot of the blueprint functions cleaner
UENUM()
enum class EBPXRResultSwitch : uint8
{
// On Success
OnSucceeded,
// On Failure
OnFailed
};
UENUM(BlueprintType)
enum class EVRSkeletalHandIndex : uint8
{
EActionHandIndex_Left = 0,
EActionHandIndex_Right
};
UENUM(BlueprintType)
enum class EXRHandJointType : uint8
{
OXR_HAND_JOINT_PALM_EXT = 0,
OXR_HAND_JOINT_WRIST_EXT = 1,
OXR_HAND_JOINT_THUMB_METACARPAL_EXT = 2,
OXR_HAND_JOINT_THUMB_PROXIMAL_EXT = 3,
OXR_HAND_JOINT_THUMB_DISTAL_EXT = 4,
OXR_HAND_JOINT_THUMB_TIP_EXT = 5,
OXR_HAND_JOINT_INDEX_METACARPAL_EXT = 6,
OXR_HAND_JOINT_INDEX_PROXIMAL_EXT = 7,
OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT = 8,
OXR_HAND_JOINT_INDEX_DISTAL_EXT = 9,
OXR_HAND_JOINT_INDEX_TIP_EXT = 10,
OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT = 11,
OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT = 12,
OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT = 13,
OXR_HAND_JOINT_MIDDLE_DISTAL_EXT = 14,
OXR_HAND_JOINT_MIDDLE_TIP_EXT = 15,
OXR_HAND_JOINT_RING_METACARPAL_EXT = 16,
OXR_HAND_JOINT_RING_PROXIMAL_EXT = 17,
OXR_HAND_JOINT_RING_INTERMEDIATE_EXT = 18,
OXR_HAND_JOINT_RING_DISTAL_EXT = 19,
OXR_HAND_JOINT_RING_TIP_EXT = 20,
OXR_HAND_JOINT_LITTLE_METACARPAL_EXT = 21,
OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT = 22,
OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT = 23,
OXR_HAND_JOINT_LITTLE_DISTAL_EXT = 24,
OXR_HAND_JOINT_LITTLE_TIP_EXT = 25,
OXR_HAND_JOINT_MAX_ENUM_EXT = 0xFF
};
UENUM(BlueprintType)
enum class EVROpenXRSkeletonType : uint8
{
// UE4 Skeletal Right hand
OXR_SkeletonType_UE4Default_Right,
// UE4 Skeletal Left hand
OXR_SkeletonType_UE4Default_Left,
// OpenVR Skeletal Right hand
OXR_SkeletonType_OpenVRDefault_Right,
// OpenVR Skeletal Left hand
OXR_SkeletonType_OpenVRDefault_Left,
// UE5 Skeletal Right hand
OXR_SkeletonType_UE5Default_Right,
// UE5 Skeletal Left hand
OXR_SkeletonType_UE5Default_Left,
// OpenXR Skeletal Right hand
OXR_SkeletonType_OpenXRDefault_Right,
// OpenXR Skeletal Left hand
OXR_SkeletonType_OpenXRDefault_Left,
OXR_SkeletonType_Custom
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRActionSkeletalData
{
GENERATED_BODY()
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;
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bAllowDeformingMesh;
// If true then the bones will be mirrored from left/right, to allow you to swap a hand mesh to the other hand
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bMirrorLeftRight;
// List of aproximated curls for each finger
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<float> FingerCurls;
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<FTransform> SkeletalTransforms;
// If true we will assume that the target skeleton does not have the metacarpal bones and we will not replicate them
// Only really used for the old UE4 skeleton
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bEnableUE4HandRepSavings;
//UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
//TArray<FTransform> OldSkeletalTransforms;
// The rotation required to rotate the finger bones back to X+
// The animation node attempts to auto calculate it, if you have a non standard hand you may need to fill
// This in by yourself
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
FTransform AdditionTransform;
UPROPERTY(NotReplicated, BlueprintReadOnly, Category = Default)
bool bHasValidData;
FName LastHandGesture;
int32 LastHandGestureIndex;
FBPOpenXRActionSkeletalData()
{
//bGetTransformsInParentSpace = false;
AdditionTransform = FTransform::Identity;// FTransform(FRotator(180.f, 0.f, -90.f), FVector::ZeroVector, FVector(1.f));//FTransform(FRotator(0.f, 90.f, 90.f), FVector::ZeroVector, FVector(1.f));
WorldScaleOverride = 0.0f;
bAllowDeformingMesh = true;
bMirrorLeftRight = false;
bEnableUE4HandRepSavings = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
PoseType = EXRControllerPoseType::Grip;
bHasValidData = false;
LastHandGestureIndex = INDEX_NONE;
LastHandGesture = NAME_None;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalPair
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EXRHandJointType OpenXRBone;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
FName BoneToTarget;
FBoneReference ReferenceToConstruct;
FCompactPoseBoneIndex ParentReference;
FQuat RetargetRot;
FBPOpenXRSkeletalPair() :
ParentReference(INDEX_NONE)
{
OpenXRBone = EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT;
BoneToTarget = NAME_None;
RetargetRot = FQuat::Identity;
}
FBPOpenXRSkeletalPair(EXRHandJointType Bone, FString TargetBone) :
ParentReference(INDEX_NONE)
{
OpenXRBone = Bone;
BoneToTarget = FName(*TargetBone);
ReferenceToConstruct.BoneName = BoneToTarget;
RetargetRot = FQuat::Identity;
}
FORCEINLINE bool operator==(const int32& Other) const
{
return ReferenceToConstruct.CachedCompactPoseIndex.GetInt() == Other;
//return ReferenceToConstruct.BoneIndex == Other;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalMappingData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
TArray<FBPOpenXRSkeletalPair> BonePairs;
TArray<int32> ReverseBonePairMap;
// Merge the transforms of bones that are missing from the OpenVR skeleton to the UE4 one.
// This should be always enabled for UE4 skeletons generally.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
bool bMergeMissingBonesUE4;
// The hand data to get, if not using a custom bone mapping then this value will be auto filled
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EVRSkeletalHandIndex TargetHand;
FQuat AdjustmentQuat;
bool bInitialized;
FName LastInitializedName;
EVROpenXRSkeletonType LastInitializedSkeleton;
void ClearMapping()
{
bInitialized = false;
LastInitializedName = NAME_None;
AdjustmentQuat = FQuat::Identity;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
BonePairs.Empty();
ReverseBonePairMap.Empty();
}
void ConstructReverseMapping()
{
int32 MaxElements = ((uint8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT) + 1;
ReverseBonePairMap.Empty(MaxElements);
ReverseBonePairMap.AddUninitialized(MaxElements);
FMemory::Memset(ReverseBonePairMap.GetData(), 0, MaxElements * sizeof(int32));
for (int i = 0; i < BonePairs.Num(); ++i)
{
// Just in case someone messed up the mapping file
if (i < MaxElements)
{
ReverseBonePairMap[(uint8)BonePairs[i].OpenXRBone] = i;
}
}
}
void ConstructDefaultMappings(EVROpenXRSkeletonType SkeletonType, bool bSkipRootBone)
{
switch (SkeletonType)
{
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenVRInputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right:
{
bMergeMissingBonesUE4 = true;
SetDefaultUE4Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultUE5Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenXRInputs(SkeletonType, bSkipRootBone);
}break;
}
}
void SetDefaultOpenVRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default OpenVR bones mapping
//if (!bSkipRootBone)
//{
//BonePairs.Add(FBPOpenVRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_PALM_EXT, FString::Printf(TEXT("Root"), HandDelimiter)));
//}
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("wrist_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("finger_thumb_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("finger_thumb_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT, FString::Printf(TEXT("finger_thumb_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, FString::Printf(TEXT("finger_index_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("finger_index_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_index_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT, FString::Printf(TEXT("finger_index_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, FString::Printf(TEXT("finger_middle_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_middle_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_middle_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT, FString::Printf(TEXT("finger_middle_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, FString::Printf(TEXT("finger_ring_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("finger_ring_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_ring_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT, FString::Printf(TEXT("finger_ring_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, FString::Printf(TEXT("finger_pinky_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_pinky_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_pinky_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT, FString::Printf(TEXT("finger_pinky_%s_end"), HandDelimiter)));
// Aux bones share the final knuckles location / rotation
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_%s_aux"), HandDelimiter)));
}
void SetDefaultUE4Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue4 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultUE5Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultOpenXRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoyying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
FBPOpenXRSkeletalMappingData()
{
AdjustmentQuat = FQuat::Identity;
bInitialized = false;
bMergeMissingBonesUE4 = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
LastInitializedName = NAME_None;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
}
};

View File

@@ -0,0 +1,377 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Engine/Texture.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
//#include "Runtime/Launch/Resources/Version.h"
#include "Animation/AnimInstanceProxy.h"
#include "OpenXRExpansionTypes.h"
#include "Engine/DataAsset.h"
#include "OpenXRHandPoseComponent.generated.h"
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPXRSkeletalRepContainer
{
GENERATED_BODY()
public:
UPROPERTY(Transient, NotReplicated)
EVRSkeletalHandIndex TargetHand;
UPROPERTY(Transient, NotReplicated)
bool bAllowDeformingMesh;
// If true we will skip sending the 4 metacarpal bones that ue4 doesn't need, (STEAMVR skeletons need this disabled!)
// Only really used for the older UE4 skeleton
UPROPERTY(Transient, NotReplicated)
bool bEnableUE4HandRepSavings;
UPROPERTY(Transient, NotReplicated)
TArray<FTransform> SkeletalTransforms;
UPROPERTY(Transient, NotReplicated)
uint8 BoneCount;
FBPXRSkeletalRepContainer()
{
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
bAllowDeformingMesh = false;
bEnableUE4HandRepSavings = false;
BoneCount = 0;
}
bool bHasValidData()
{
return SkeletalTransforms.Num() > 0;
}
void CopyForReplication(FBPOpenXRActionSkeletalData& Other);
static void CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other);
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};
template<>
struct TStructOpsTypeTraits< FBPXRSkeletalRepContainer > : public TStructOpsTypeTraitsBase2<FBPXRSkeletalRepContainer>
{
enum
{
WithNetSerializer = true
};
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGestureFingerPosition
{
GENERATED_BODY()
public:
// The Finger index, not editable
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "VRGesture")
EXRHandJointType IndexType;
// The locational value of this element 0.f - 1.f
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FVector Value;
// The threshold within which this finger value will be detected as matching (1.0 would be always matching, IE: finger doesn't count)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture", meta = (ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
float Threshold;
FOpenXRGestureFingerPosition(FVector TipLoc, EXRHandJointType Type)
{
IndexType = Type;
Value = TipLoc;
Threshold = 5.0f;
}
FOpenXRGestureFingerPosition()
{
IndexType = EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT;
Value = FVector(0.f);
Threshold = 5.0f;
}
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGesture
{
GENERATED_BODY()
public:
// Name of the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FName Name;
// Samples in the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
TArray<FOpenXRGestureFingerPosition> FingerValues;
FOpenXRGesture()
{
InitPoseValues();
Name = NAME_None;
}
void InitPoseValues()
{
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT));
}
};
/**
* Items Database DataAsset, here we can save all of our game items
*/
UCLASS(BlueprintType, Category = "VRGestures")
class OPENXREXPANSIONPLUGIN_API UOpenXRGestureDatabase : public UDataAsset
{
GENERATED_BODY()
public:
// Gestures in this database
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
TArray <FOpenXRGesture> Gestures;
UOpenXRGestureDatabase()
{
}
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureDetected, const FName &, GestureDetected, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureEnded, const FName &, GestureEnded, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRHandPoseComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer);
// Says whether we should run gesture detection
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
bool bDetectGestures;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
void SetDetectGestures(bool bNewDetectGestures)
{
bDetectGestures = bNewDetectGestures;
}
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureDetected OnNewGestureDetected;
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureEnded OnGestureEnded;
// Known sequences
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UOpenXRGestureDatabase *GesturesDB;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
bool SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave = EVRSkeletalHandIndex::EActionHandIndex_Right);
UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (DisplayName = "DetectCurrentPose"))
bool K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut);
// This version throws events
bool DetectCurrentPose(FBPOpenXRActionSkeletalData& SkeletalAction);
// Need this as I can't think of another way for an actor component to make sure it isn't on the server
inline bool IsLocallyControlled() const
{
//#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
const AActor* MyOwner = GetOwner();
return MyOwner->HasLocalNetOwner();
//#else
// I like epics new authority check more than mine
/* const AActor* MyOwner = GetOwner();
const APawn* MyPawn = Cast<APawn>(MyOwner);
return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);*/
//#endif
}
// Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that
void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
//virtual void OnUnregister() override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
bool bGetMockUpPoseForDebugging;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActions;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformLeft)
FBPXRSkeletalRepContainer LeftHandRep;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformRight)
FBPXRSkeletalRepContainer RightHandRep;
UFUNCTION(Unreliable, Server, WithValidation)
void Server_SendSkeletalTransforms(const FBPXRSkeletalRepContainer& SkeletalInfo);
bool bLerpingPositionLeft;
bool bLerpingPositionRight;
struct FTransformLerpManager
{
bool bReplicatedOnce;
bool bLerping;
float UpdateCount;
float UpdateRate;
TArray<FTransform> OldTransforms;
TArray<FTransform> NewTransforms;
FTransformLerpManager();
void PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
void NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
FORCEINLINE void BlendBone(uint8 BoneToBlend, FBPOpenXRActionSkeletalData& ActionInfo, float& LerpVal, bool bExponentialSmoothing)
{
ActionInfo.SkeletalTransforms[BoneToBlend].Blend(OldTransforms[BoneToBlend], NewTransforms[BoneToBlend], LerpVal);
if (bExponentialSmoothing)
{
// Saving base back out for exponential
OldTransforms[BoneToBlend] = ActionInfo.SkeletalTransforms[BoneToBlend];
}
}
void UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent * ParentComp);
};
FTransformLerpManager LeftHandRepManager;
FTransformLerpManager RightHandRepManager;
UFUNCTION()
virtual void OnRep_SkeletalTransformLeft()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == LeftHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(LeftHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
UFUNCTION()
virtual void OnRep_SkeletalTransformRight()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == RightHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(RightHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
// If we should replicate the skeletal transform data
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bReplicateSkeletalData;
// If true we will lerp between updates of the skeletal mesh transforms and smooth the result
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bSmoothReplicatedSkeletalData;
// If true then we will use exponential smoothing with buffered correction
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bSmoothReplicatedSkeletalData"))
bool bUseExponentialSmoothing = false;
// Timestep of smoothing translation
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bUseExponentialSmoothing"))
float InterpolationSpeed = 25.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SkeletalData)
float ReplicationRateForSkeletalAnimations;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalNetUpdateCount;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalUpdateCount;
};
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FOpenXRAnimInstanceProxy : public FAnimInstanceProxy
{
public:
GENERATED_BODY()
FOpenXRAnimInstanceProxy() {}
FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance);
/** Called before update so we can copy any data we need */
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
public:
EVRSkeletalHandIndex TargetHand;
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActionData;
};
UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
class OPENXREXPANSIONPLUGIN_API UOpenXRAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(transient)
UOpenXRHandPoseComponent * OwningPoseComp;
FOpenXRAnimInstanceProxy AnimInstanceProxy;
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
{
return new FOpenXRAnimInstanceProxy(this);
//return &AnimInstanceProxy;
}
virtual void NativeBeginPlay() override;
//virtual void NativeInitializeAnimation() override;
UFUNCTION(BlueprintCallable, Category = "BoneMappings")
void InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData & SkeletalMappingData);
};

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);
}

View File

@@ -0,0 +1,48 @@
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.

View File

@@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,849 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HandSocketComponentDetails.h"
#include "HandSocketVisualizer.h"
//#include "PropertyEditing.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
#include "Developer/AssetTools/Public/IAssetTools.h"
#include "Developer/AssetTools/Public/AssetToolsModule.h"
#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
#include "AnimationUtils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "UObject/SavePackage.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "Styling/CoreStyle.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#define LOCTEXT_NAMESPACE "HandSocketComponentDetails"
FText SCreateHandAnimationDlg::LastUsedAssetPath;
static bool PromptUserForAssetPath(FString& AssetPath, FString& AssetName)
{
TSharedRef<SCreateHandAnimationDlg> NewAnimDlg = SNew(SCreateHandAnimationDlg);
if (NewAnimDlg->ShowModal() != EAppReturnType::Cancel)
{
AssetPath = NewAnimDlg->GetFullAssetPath();
AssetName = NewAnimDlg->GetAssetName();
return true;
}
return false;
}
TWeakObjectPtr<UAnimSequence> FHandSocketComponentDetails::SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName)
{
TWeakObjectPtr<UAnimSequence> FinalAnimation;
// Replace when this moves to custom display
if (!HandSocketComponent.IsValid())
return FinalAnimation;
/*if (!HandSocketComponent->HandVisualizerComponent)// || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh->Skeleton)
{
return false;
}*/
if (!HandSocketComponent->HandTargetAnimation && (!HandSocketComponent->VisualizationMesh || !HandSocketComponent->VisualizationMesh->GetSkeleton()))
{
return FinalAnimation;
}
// create the asset
FText InvalidPathReason;
bool const bValidPackageName = FPackageName::IsValidLongPackageName(InAssetPath, false, &InvalidPathReason);
if (bValidPackageName == false)
{
UE_LOG(LogAnimation, Log, TEXT("%s is an invalid asset path, prompting user for new asset path. Reason: %s"), *InAssetPath, *InvalidPathReason.ToString());
}
FString ValidatedAssetPath = InAssetPath;
FString ValidatedAssetName = InAssetName;
UObject* Parent = bValidPackageName ? CreatePackage(*ValidatedAssetPath) : nullptr;
if (Parent == nullptr)
{
// bad or no path passed in, do the popup
if (PromptUserForAssetPath(ValidatedAssetPath, ValidatedAssetName) == false)
{
return FinalAnimation;
}
Parent = CreatePackage(*ValidatedAssetPath);
}
UObject* const Object = LoadObject<UObject>(Parent, *ValidatedAssetName, nullptr, LOAD_Quiet, nullptr);
// if object with same name exists, warn user
if (Object)
{
EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("Error_AssetExist", "Asset with same name exists. Do you wish to overwrite it?"));
if (ReturnValue == EAppReturnType::No)
{
return FinalAnimation; // failed
}
}
UAnimSequence* BaseAnimation = HandSocketComponent->HandTargetAnimation;
TArray<FTransform> LocalPoses;
if (!BaseAnimation)
{
LocalPoses = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetRefBonePose();
}
// If not, create new one now.
UAnimSequence* const NewSeq = NewObject<UAnimSequence>(Parent, *ValidatedAssetName, RF_Public | RF_Standalone);
if (NewSeq)
{
// set skeleton
if (BaseAnimation)
{
NewSeq->SetSkeleton(BaseAnimation->GetSkeleton());
}
else
{
NewSeq->SetSkeleton(HandSocketComponent->VisualizationMesh->GetSkeleton());
}
// Notify the asset registry
FAssetRegistryModule::AssetCreated(NewSeq);
//StartRecord(Component, NewSeq);
//return true;
UAnimSequence* AnimationObject = NewSeq;
IAnimationDataController& AnimController = AnimationObject->GetController();
{
IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("SaveAnimationAsset_VRE", "Creating Animation Sequence based on hand pose"));
AnimationObject->ResetAnimation();
if (BaseAnimation)
{
AnimationObject->BoneCompressionSettings = BaseAnimation->BoneCompressionSettings;
}
else
{
AnimationObject->BoneCompressionSettings = FAnimationUtils::GetDefaultAnimationBoneCompressionSettings();
}
AnimController.InitializeModel();
AnimController.RemoveAllBoneTracks(false);
//checkf(MovieScene, TEXT("No Movie Scene found for SequencerDataModel"));
//AnimController.SetPlayLength(4.f);
AnimController.SetNumberOfFrames(FFrameNumber(1), false);
// Set frame rate to 1 to 1 as we don't animate
AnimController.SetFrameRate(FFrameRate(1, 1));
TArray<FName> TrackNames;
const IAnimationDataModel* BaseDataModel = BaseAnimation ? BaseAnimation->GetController().GetModel() : nullptr;
if (BaseAnimation)
{
if (BaseDataModel)
{
BaseDataModel->GetBoneTrackNames(TrackNames);
for (FName TrackName : TrackNames)
{
AnimController.AddBoneCurve(TrackName);
}
}
else
{
return FinalAnimation;
}
}
else
{
int numBones = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetNum();
for (int i = 0; i < LocalPoses.Num() && i < numBones; ++i)
{
AnimController.AddBoneCurve(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
//AnimController.AddBoneTrack(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
}
}
if (BaseAnimation)
{
AnimationObject->RetargetSource = BaseAnimation->RetargetSource;
}
else
{
AnimationObject->RetargetSource = HandSocketComponent->VisualizationMesh ? HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRetargetSourceForMesh(HandSocketComponent->VisualizationMesh) : NAME_None;
}
const IAnimationDataModel* DataModel = AnimController.GetModel();
/// SAVE POSE
if (BaseAnimation && DataModel && BaseDataModel)
{
for (int32 TrackIndex = 0; TrackIndex < /*DataModel->GetBoneAnimationTracks().Num()*/BaseDataModel->GetNumBoneTracks(); ++TrackIndex)
{
FName TrackName = TrackIndex < TrackNames.Num() ? TrackNames[TrackIndex] : NAME_None;
if (!BaseDataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
FTransform FinalTrans = BaseDataModel->GetBoneTrackTransform(TrackName, 0);
//FTransform FinalTrans(Rot, Loc, Scale);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == TrackName)
{
DeltaQuat = HandPair.DeltaPose;
break;
}
}
FinalTrans.ConcatenateRotation(DeltaQuat);
FinalTrans.NormalizeRotation();
//FRawAnimSequenceTrack& RawNewTrack = DataModel->GetBoneTrackByIndex(TrackIndex).InternalTrackData;
AnimController.SetBoneTrackKeys(TrackName, { FinalTrans.GetTranslation() }, { FinalTrans.GetRotation() }, { FinalTrans.GetScale3D() });
}
}
else if(DataModel)
{
USkeletalMesh* SkeletalMesh = HandSocketComponent->VisualizationMesh;
FReferenceSkeleton RefSkeleton = SkeletalMesh->GetRefSkeleton();
USkeleton* AnimSkeleton = SkeletalMesh->GetSkeleton();
for (int32 TrackIndex = 0; TrackIndex < RefSkeleton.GetNum(); ++TrackIndex)
{
FName TrackName = RefSkeleton.GetBoneName(TrackIndex);
if (!DataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(TrackName);
// verify if this bone exists in skeleton
//int32 BoneTreeIndex = DataModel->GetBoneTrackByIndex(TrackIndex).BoneTreeIndex;
if (BoneTreeIndex != INDEX_NONE)
{
int32 BoneIndex = BoneTreeIndex;//AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
//int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
FTransform LocalTransform = LocalPoses[BoneIndex];
//FTransform LocalTransform = RefSkeleton.GetRefBonePose()[BoneIndex];
FName BoneName = AnimSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == BoneName)
{
DeltaQuat = HandPair.DeltaPose;
}
}
LocalTransform.ConcatenateRotation(DeltaQuat);
LocalTransform.NormalizeRotation();
AnimController.SetBoneTrackKeys(BoneName, { LocalTransform.GetTranslation() }, { LocalTransform.GetRotation() }, { LocalTransform.GetScale3D() });
}
}
}
AnimController.NotifyPopulated();
}
/// END SAVE POSE
///
///
///
// init notifies
AnimationObject->InitializeNotifyTrack();
//#TODO: 5.1, need to figure out what they replaced this with
//PRAGMA_DISABLE_DEPRECATION_WARNINGS
//AnimationObject->PostProcessSequence();
//PRAGMA_ENABLE_DEPRECATION_WARNINGS
AnimationObject->MarkPackageDirty();
//if (bAutoSaveAsset)
{
UPackage* const Package = AnimationObject->GetOutermost();
FString const PackageName = Package->GetName();
FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
double StartTime = FPlatformTime::Seconds();
FSavePackageArgs PackageArguments;
PackageArguments.SaveFlags = RF_Standalone;
PackageArguments.SaveFlags = SAVE_NoError;
UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
//UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
double ElapsedTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogAnimation, Log, TEXT("Animation Recorder saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
}
FinalAnimation = AnimationObject;
return FinalAnimation;
}
return FinalAnimation;
}
TSharedRef< IDetailCustomization > FHandSocketComponentDetails::MakeInstance()
{
return MakeShareable(new FHandSocketComponentDetails);
}
void FHandSocketComponentDetails::OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
void FHandSocketComponentDetails::OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
// Default to always flipping this
//if (HandSocketComponent->bFlipForLeftHand)
{
FTransform relTrans = HandSocketComponent->GetRelativeTransform();
FTransform HandPlacement = HandSocketComponent->GetHandRelativePlacement();
if (HandSocketComponent->bDecoupleMeshPlacement)
{
relTrans = FTransform::Identity;
}
FTransform ReturnTrans = (HandPlacement * relTrans);
HandSocketComponent->MirrorHandTransform(ReturnTrans, relTrans);
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
ReturnTrans = ReturnTrans.GetRelativeTransform(relTrans);
HandSocketComponent->HandRelativePlacement = ReturnTrans;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
void FHandSocketComponentDetails::OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
if (HandSocketComponent->bDecoupleMeshPlacement)
{
//FTransform RelTrans = HandSocketComponent->GetRelativeTransform();
//FTransform WorldTrans = HandSocketComponent->GetComponentTransform();
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement * HandSocketComponent->GetRelativeTransform();// HandSocketComponent->GetComponentTransform();
HandSocketComponent->bDecoupled = true;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = true;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
else
{
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement.GetRelativeTransform(HandSocketComponent->GetRelativeTransform());
HandSocketComponent->bDecoupled = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = false;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupled)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
void FHandSocketComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
// Hide the SplineCurves property
//TSharedPtr<IPropertyHandle> HandPlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
//HandPlacementProperty->MarkHiddenByCustomization();
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
if (ObjectsBeingCustomized.Num() == 1)
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(ObjectsBeingCustomized[0]);
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
}
}
/*const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetSelectedObjects();
for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
{
const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
if (CurrentObject.IsValid())
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(CurrentObject.Get());
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
break;
}
}
}*/
DetailBuilder.HideCategory(FName("ComponentTick"));
DetailBuilder.HideCategory(FName("GameplayTags"));
DetailBuilder.HideCategory(FName("VRGripInterface"));
DetailBuilder.HideCategory(FName("VRGripInterface|Replication"));
DetailBuilder.HideCategory(FName("Tags"));
DetailBuilder.HideCategory(FName("AssetUserData"));
DetailBuilder.HideCategory(FName("Events"));
DetailBuilder.HideCategory(FName("Activation"));
DetailBuilder.HideCategory(FName("Cooking"));
DetailBuilder.HideCategory(FName("ComponentReplication"));
TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
TSharedPtr<IPropertyHandle> HandRelativePlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
TSharedPtr<IPropertyHandle> LeftHandDominateProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bLeftHandDominant));
FSimpleDelegate OnHandRelativeChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnHandRelativeUpdated, &DetailBuilder);
HandRelativePlacementProperty->SetOnPropertyValueChanged(OnHandRelativeChangedDelegate);
FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLockedStateUpdated, &DetailBuilder);
LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
FSimpleDelegate OnLeftDominateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLeftDominantUpdated, &DetailBuilder);
LeftHandDominateProperty->SetOnPropertyValueChanged(OnLeftDominateChangedDelegate);
TSharedPtr<IPropertyHandle> ShowVisualizationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bShowVisualizationMesh));
FSimpleDelegate OnShowVisChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnUpdateShowMesh, &DetailBuilder);
ShowVisualizationProperty->SetOnPropertyValueChanged(OnShowVisChangedDelegate);
DetailBuilder.EditCategory("Hand Animation")
.AddCustomRow(LOCTEXT("UpdateHandSocketRow", "Save Current Pose"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("UpdateHandSocketText", "Save Current Pose"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FHandSocketComponentDetails::OnUpdateSavePose)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("UpdateHandSocketButton", "Save"))
]
];
}
void FHandSocketComponentDetails::OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
return;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
}
}
FReply FHandSocketComponentDetails::OnUpdateSavePose()
{
if (HandSocketComponent.IsValid() && HandSocketComponent->CustomPoseDeltas.Num() > 0)
{
if (HandSocketComponent->HandTargetAnimation || HandSocketComponent->VisualizationMesh)
{
// Save Animation Pose here
FString AssetPath;
FString AssetName;
PromptUserForAssetPath(AssetPath, AssetName);
TWeakObjectPtr<UAnimSequence> NewAnim = SaveAnimationAsset(AssetPath, AssetName);
// Finally remove the deltas
if (NewAnim.IsValid())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandTargetAnimation = NewAnim.Get();
HandSocketComponent->CustomPoseDeltas.Empty();
HandSocketComponent->bUseCustomPoseDeltas = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandTargetAnimation = NewAnim.Get();
RefHand->CustomPoseDeltas.Empty();
RefHand->bUseCustomPoseDeltas = false;
/*TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(RefHand, PropertiesToModify);*/
}
}
// Modify all of the properties at once
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
}
}
return FReply::Handled();
}
void SCreateHandAnimationDlg::Construct(const FArguments& InArgs)
{
AssetPath = FText::FromString(FPackageName::GetLongPackagePath(InArgs._DefaultAssetPath.ToString()));
AssetName = FText::FromString(FPackageName::GetLongPackageAssetName(InArgs._DefaultAssetPath.ToString()));
if (AssetPath.IsEmpty())
{
AssetPath = LastUsedAssetPath;
// still empty?
if (AssetPath.IsEmpty())
{
AssetPath = FText::FromString(TEXT("/Game"));
}
}
else
{
LastUsedAssetPath = AssetPath;
}
if (AssetName.IsEmpty())
{
// find default name for them
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
FString OutPackageName, OutAssetName;
FString PackageName = AssetPath.ToString() + TEXT("/NewAnimation");
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), OutPackageName, OutAssetName);
AssetName = FText::FromString(OutAssetName);
}
FPathPickerConfig PathPickerConfig;
PathPickerConfig.DefaultPath = AssetPath.ToString();
PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &SCreateHandAnimationDlg::OnPathChange);
PathPickerConfig.bAddDefaultPath = true;
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
SWindow::Construct(SWindow::FArguments()
.Title(LOCTEXT("SCreateHandAnimationDlg_Title", "Create New Animation Object"))
.SupportsMinimize(false)
.SupportsMaximize(false)
//.SizingRule( ESizingRule::Autosized )
.ClientSize(FVector2D(450, 450))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot() // Add user input block
.Padding(2)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(LOCTEXT("SelectPath", "Select Path to create animation"))
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 14))
]
+ SVerticalBox::Slot()
.FillHeight(1)
.Padding(3)
[
ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(3)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 10, 0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("AnimationName", "Animation Name"))
]
+ SHorizontalBox::Slot()
[
SNew(SEditableTextBox)
.Text(AssetName)
.OnTextCommitted(this, &SCreateHandAnimationDlg::OnNameChange)
.MinDesiredWidth(250)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(5)
[
SNew(SUniformGridPanel)
.SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding"))
.MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
.MinDesiredSlotHeight(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
+ SUniformGridPanel::Slot(0, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("OK", "OK"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Ok)
]
+ SUniformGridPanel::Slot(1, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("Cancel", "Cancel"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Cancel)
]
]
]);
}
void SCreateHandAnimationDlg::OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo)
{
AssetName = NewName;
}
void SCreateHandAnimationDlg::OnPathChange(const FString& NewPath)
{
AssetPath = FText::FromString(NewPath);
LastUsedAssetPath = AssetPath;
}
FReply SCreateHandAnimationDlg::OnButtonClick(EAppReturnType::Type ButtonID)
{
UserResponse = ButtonID;
if (ButtonID != EAppReturnType::Cancel)
{
if (!ValidatePackage())
{
// reject the request
return FReply::Handled();
}
}
RequestDestroyWindow();
return FReply::Handled();
}
/** Ensures supplied package name information is valid */
bool SCreateHandAnimationDlg::ValidatePackage()
{
FText Reason;
FString FullPath = GetFullAssetPath();
if (!FPackageName::IsValidLongPackageName(FullPath, false, &Reason)
|| !FName(*AssetName.ToString()).IsValidObjectName(Reason))
{
FMessageDialog::Open(EAppMsgType::Ok, Reason);
return false;
}
return true;
}
EAppReturnType::Type SCreateHandAnimationDlg::ShowModal()
{
GEditor->EditorAddModalWindow(SharedThis(this));
return UserResponse;
}
FString SCreateHandAnimationDlg::GetAssetPath()
{
return AssetPath.ToString();
}
FString SCreateHandAnimationDlg::GetAssetName()
{
return AssetName.ToString();
}
FString SCreateHandAnimationDlg::GetFullAssetPath()
{
return AssetPath.ToString() + "/" + AssetName.ToString();
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,484 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HandSocketVisualizer.h"
#include "CanvasItem.h"
#include "CanvasTypes.h"
#include "SceneManagement.h"
//#include "UObject/Field.h"
#include "VRBPDatatypes.h"
#include "ScopedTransaction.h"
#include "Modules/ModuleManager.h"
#include "EditorViewportClient.h"
#include "Components/PoseableMeshComponent.h"
#include "Misc/PackageName.h"
//#include "Persona.h"
IMPLEMENT_HIT_PROXY(HHandSocketVisProxy, HComponentVisProxy);
#define LOCTEXT_NAMESPACE "HandSocketVisualizer"
bool FHandSocketVisualizer::VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click)
{
bool bEditing = false;
if (VisProxy && VisProxy->Component.IsValid())
{
bEditing = true;
if (VisProxy->IsA(HHandSocketVisProxy::StaticGetType()))
{
if( const UHandSocketComponent * HandComp = UpdateSelectedHandComponent(VisProxy))
{
HHandSocketVisProxy* Proxy = (HHandSocketVisProxy*)VisProxy;
if (Proxy)
{
CurrentlySelectedBone = Proxy->TargetBoneName;
CurrentlySelectedBoneIdx = Proxy->BoneIdx;
TargetViewport = InViewportClient->Viewport;
}
}
}
}
return bEditing;
}
bool FHandSocketVisualizer::GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None/* && CurrentlySelectedBone != "HandSocket"*/)
{
if (CurrentlySelectedBone == "HandSocket")
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (CurrentlyEditingComponent)
{
if (CurrentlyEditingComponent->bMirrorVisualizationMesh)
{
FTransform NewTrans = CurrentlyEditingComponent->GetRelativeTransform();
NewTrans.Mirror(CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->MirrorAxis), CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->FlipAxis));
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
NewTrans = NewTrans * ParentComp->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(NewTrans.GetRotation());
}
}
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(newTrans.GetRotation());
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
FTransform newTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
FQuat Rot = newTrans.GetRotation();
if (!newTrans.GetRotation().ContainsNaN())
{
Rot.Normalize();
OutMatrix = FRotationMatrix::Make(Rot);
return true;
}
}
return false;
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::IsVisualizingArchetype() const
{
return (HandPropertyPath.IsValid() && HandPropertyPath.GetParentOwningActor() && FActorEditorUtils::IsAPreviewOrInactiveActor(HandPropertyPath.GetParentOwningActor()));
}
void FHandSocketVisualizer::DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return;
}
if (const UHandSocketComponent* HandComp = Cast<const UHandSocketComponent>(Component))
{
if (CurrentlySelectedBone != NAME_None)
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (!IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
return;
}
int32 XL;
int32 YL;
const FIntRect CanvasRect = Canvas->GetViewRect();
FPlane location = View->Project(CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation());
StringSize(GEngine->GetLargeFont(), XL, YL, *CurrentlySelectedBone.ToString());
//const float DrawPositionX = location.X - XL;
//const float DrawPositionY = location.Y - YL;
const float DrawPositionX = FMath::FloorToFloat(CanvasRect.Min.X + (CanvasRect.Width() - XL) * 0.5f);
const float DrawPositionY = CanvasRect.Min.Y + 50.0f;
Canvas->DrawShadowedString(DrawPositionX, DrawPositionY, *CurrentlySelectedBone.ToString(), GEngine->GetLargeFont(), FLinearColor::Yellow);
}
}
}
}
void FHandSocketVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
//UWorld* World = Component->GetWorld();
//return World && (World->WorldType == EWorldType::EditorPreview || World->WorldType == EWorldType::Inactive);
//cast the component into the expected component type
if (const UHandSocketComponent* HandComponent = Cast<UHandSocketComponent>(Component))
{
if (!HandComponent->HandVisualizerComponent)
return;
//This is an editor only uproperty of our targeting component, that way we can change the colors if we can't see them against the background
const FLinearColor SelectedColor = FLinearColor::Yellow;//TargetingComponent->EditorSelectedColor;
const FLinearColor UnselectedColor = FLinearColor::White;//TargetingComponent->EditorUnselectedColor;
const FVector Location = HandComponent->HandVisualizerComponent->GetComponentLocation();
float BoneScale = 1.0f - ((View->ViewLocation - Location).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
HHandSocketVisProxy* newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "Visualizer";
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(Location, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : FLinearColor::Red, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "HandSocket";
BoneScale = 1.0f - ((View->ViewLocation - HandComponent->GetComponentLocation()).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(HandComponent->GetComponentLocation(), FLinearColor::Green, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
if (HandComponent->bUseCustomPoseDeltas)
{
TArray<FTransform> BoneTransforms = HandComponent->HandVisualizerComponent->GetBoneSpaceTransforms();
FTransform ParentTrans = HandComponent->HandVisualizerComponent->GetComponentTransform();
// We skip root bone, moving the visualizer itself handles that
for (int i = 1; i < HandComponent->HandVisualizerComponent->GetNumBones(); i++)
{
FName BoneName = HandComponent->HandVisualizerComponent->GetBoneName(i);
if (HandComponent->bFilterBonesByPostfix)
{
if (BoneName.ToString().Right(2) != HandComponent->FilterPostfix)
{
// Skip visualizing this bone its the incorrect side
continue;
}
}
if (HandComponent->BonesToSkip.Contains(BoneName))
{
// Skip visualizing this bone as its in the ignore array
continue;
}
FTransform BoneTransform = HandComponent->HandVisualizerComponent->GetBoneTransform(i);
FVector BoneLoc = BoneTransform.GetLocation();
BoneScale = 1.0f - ((View->ViewLocation - BoneLoc).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.1f, 0.9f);
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = BoneName;
newHitProxy->BoneIdx = i;
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(BoneLoc, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : UnselectedColor, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
}
}
if (HandComponent->bShowRangeVisualization)
{
float RangeVisualization = HandComponent->OverrideDistance;
if (RangeVisualization <= 0.0f)
{
if (USceneComponent* Parent = Cast<USceneComponent>(HandComponent->GetAttachParent()))
{
FStructProperty* ObjectProperty = CastField<FStructProperty>(Parent->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
AActor* ParentsActor = nullptr;
if (!ObjectProperty)
{
ParentsActor = Parent->GetOwner();
if (ParentsActor)
{
ObjectProperty = CastField<FStructProperty>(Parent->GetOwner()->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
}
}
if (ObjectProperty)
{
UObject* Target = ParentsActor;
if (Target == nullptr)
{
Target = Parent;
}
if (const FBPInterfaceProperties* Curve = ObjectProperty->ContainerPtrToValuePtr<FBPInterfaceProperties>(Target))
{
if (HandComponent->SlotPrefix == "VRGripS")
{
RangeVisualization = Curve->SecondarySlotRange;
}
else
{
RangeVisualization = Curve->PrimarySlotRange;
}
}
}
}
}
// Scale into our parents space as that is actually what the range is based on
FBox BoxToDraw = FBox::BuildAABB(FVector::ZeroVector, FVector(RangeVisualization) * HandComponent->GetAttachParent()->GetComponentScale());
BoxToDraw.Min += HandComponent->GetComponentLocation();
BoxToDraw.Max += HandComponent->GetComponentLocation();
DrawWireBox(PDI, BoxToDraw, FColor::Green, 0.0f);
}
}
}
bool FHandSocketVisualizer::GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None && CurrentlySelectedBone != "HandSocket")
{
if (CurrentlySelectedBone == "HandSocket")
{
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutLocation = newTrans.GetLocation();
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
OutLocation = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation();
}
else
{
return false;
}
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return false;
}
bool bHandled = false;
if (HandPropertyPath.IsValid())
{
if (CurrentlySelectedBone == "HandSocket" || CurrentlySelectedBone == NAME_None)
{
bHandled = false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent)
{
return false;
}
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform CurrentTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
CurrentTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
if (!DeltaTranslate.IsNearlyZero())
{
CurrentTrans.AddToTranslation(DeltaTranslate);
}
if (!DeltaRotate.IsNearlyZero())
{
CurrentTrans.SetRotation(DeltaRotate.Quaternion() * CurrentTrans.GetRotation());
}
if (!DeltaScale.IsNearlyZero())
{
CurrentTrans.MultiplyScale3D(DeltaScale);
}
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(ParentComp->GetComponentTransform());
}
}
else
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(CurrentlyEditingComponent->GetComponentTransform());
}
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
else
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent || !CurrentlyEditingComponent->HandVisualizerComponent)
{
return false;
}
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform BoneTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
FTransform NewTrans = BoneTrans;
NewTrans.SetRotation(DeltaRotate.Quaternion() * NewTrans.GetRotation());
FQuat DeltaRotateMod = NewTrans.GetRelativeTransform(BoneTrans).GetRotation();
bool bFoundBone = false;
for (FBPVRHandPoseBonePair& BonePair : CurrentlyEditingComponent->CustomPoseDeltas)
{
if (BonePair.BoneName == CurrentlySelectedBone)
{
bFoundBone = true;
BonePair.DeltaPose *= DeltaRotateMod;
break;
}
}
if (!bFoundBone)
{
FBPVRHandPoseBonePair newBonePair;
newBonePair.BoneName = CurrentlySelectedBone;
newBonePair.DeltaPose *= DeltaRotateMod;
CurrentlyEditingComponent->CustomPoseDeltas.Add(newBonePair);
bFoundBone = true;
}
if (bFoundBone)
{
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
}
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
}
return bHandled;
}
void FHandSocketVisualizer::EndEditing()
{
HandPropertyPath = FComponentPropertyPath();
CurrentlySelectedBone = NAME_None;
CurrentlySelectedBoneIdx = INDEX_NONE;
TargetViewport = nullptr;
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,73 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "VRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#include "Grippables/HandSocketComponent.h"
#include "PropertyEditorModule.h"
#include "HandSocketVisualizer.h"
#include "HandSocketComponentDetails.h"
#include "VRGlobalSettingsDetails.h"
#include "VRGlobalSettings.h"
IMPLEMENT_MODULE(FVRExpansionEditorModule, VRExpansionEditor);
void FVRExpansionEditorModule::StartupModule()
{
RegisterComponentVisualizer(UHandSocketComponent::StaticClass()->GetFName(), MakeShareable(new FHandSocketVisualizer));
// Register detail customizations
{
auto& PropertyModule = FModuleManager::LoadModuleChecked< FPropertyEditorModule >("PropertyEditor");
// Register our customization to be used by a class 'UMyClass' or 'AMyClass'. Note the prefix must be dropped.
PropertyModule.RegisterCustomClassLayout(
UHandSocketComponent::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FHandSocketComponentDetails::MakeInstance)
);
PropertyModule.RegisterCustomClassLayout(
UVRGlobalSettings::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FVRGlobalSettingsDetails::MakeInstance)
);
PropertyModule.NotifyCustomizationModuleChanged();
}
}
void FVRExpansionEditorModule::ShutdownModule()
{
if (GUnrealEd != NULL)
{
// Iterate over all class names we registered for
for (FName ClassName : RegisteredComponentClassNames)
{
GUnrealEd->UnregisterComponentVisualizer(ClassName);
}
}
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
auto& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomClassLayout(UHandSocketComponent::StaticClass()->GetFName());
PropertyModule.UnregisterCustomClassLayout(UVRGlobalSettings::StaticClass()->GetFName());
}
}
void FVRExpansionEditorModule::RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer)
{
if (GUnrealEd != NULL)
{
GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer);
}
RegisteredComponentClassNames.Add(ComponentClassName);
if (Visualizer.IsValid())
{
Visualizer->OnRegister();
}
}

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