Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UniversalObjectLocatorFragment.h"
# include "Algo/Find.h"
# include "UniversalObjectLocatorFragmentType.h"
# include "UniversalObjectLocatorStringParams.h"
# include "UniversalObjectLocatorInitializeParams.h"
# include "UniversalObjectLocatorInitializeResult.h"
2023-11-22 12:02:21 -05:00
# include "UniversalObjectLocatorRegistry.h"
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
# include "UObject/SoftObjectPath.h"
# include "Containers/SparseArray.h"
# include "Misc/AsciiSet.h"
# define LOCTEXT_NAMESPACE "UOL"
DECLARE_LOG_CATEGORY_EXTERN ( LogUniversalObjectLocator , Log , Warning ) ;
DEFINE_LOG_CATEGORY ( LogUniversalObjectLocator ) ;
namespace UE : : UniversalObjectLocator
{
Sequencer: Major integration of Universal Object Locators into Sequencer. This includes:
* Adding some first pass UI for editing the Universal Object Locators that are used to bind objects to tracks in Sequencer. This is currently in the form of:
* An 'Add Empty Binding' item in the + Actor to Sequencer menu
* A 'Binding Properties' sub-menu in the object binding track properties that allows you to modify the array of UOL's that bind objects to the track. This allows you to specifically select certain UOL types and then fill in the data that makes up that UOL. This is necessary for example for the FortAIPlayerPawn binding we plan on adding for UEFN.
* Major refactor of MovieScene binding code to more seamlessly allow for the use of the Locator resolution, especially in the case of supporting locators that spawn actors, either in the case of editor preview actors or future runtime 'spawnables'. This was necessary to consolidate the cases where locator resolve params were being created to as few places as possible for simplicity.
* Add the concept of 'default' editor and runtime flags to locators to make it easier for Sequencer to know how to interact with bindings of different types. These flags are then stored with the binding in Sequencer and editable by users as necessary. Sequencer treats 'load'/'unload' in this case similar to 'spawn'/'despawn' and generally speaking resolving a locator with editor flags of 'load' will expect the locator to spawn an actor for preview, while resolving a locator with runtime flags of 'load' will expect the locator to create a 'spawnable' character.
* To manage lifetime of these preview/spawnable characters, created an interface for a 'LocatorSpawnedCache' which the locators interact with when creating or destroying an actor. This allows the resolver of locators to differentiate between cases where they expect multiple resolves of the same locator to create duplicate actors vs. resolve to the same actor and track the lifetimes of these spawned actors.
* Sequencer implements the above cache within its own object cache in evaluation state.
* Expand the role of the previously created Binding Lifetime track to trigger loading/unloading via locators in the cache. The Binding Lifetime track will now automatically be added to a binding if its locator type contains 'Load' as a default editor or runtime flag.
* Some other editor updates to support the above.
Future issues to be resolved:
* We want to more easily expose the editing of binding properties when necessary rather than having it buried in a sub-menu. Ideas for this include some kind of UI on the track itself, for example a combo box with binding types on it, as well as potentially the properties menu appearing on hovering over the possessable icon on the object binding track. We also want to expose properties based on selection, for example object binding properties, track properties, and section properties, in another external window.
* We want to eventually replace the 'Actor to Sequencer' and 'Assign Actor' menu with this once the UX is better.
The above was all tested with a Fortnite NPC Locator type which will be added in a separate changelist.
[REVIEW] [at]ue-sequencer
#jira UE-199299
[CL 30596833 by david bromberg in ue5-main branch]
2024-01-12 12:41:00 -05:00
const FFragmentType * FindBestFragmentType ( const UObject * Object , UObject * Context )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
{
// Loop through all our FragmentTypes to find the most supported one
uint32 BestFragmentTypePriority = 0 ;
const FFragmentType * BestFragmentType = nullptr ;
2023-11-22 12:02:21 -05:00
for ( const FFragmentType & FragmentType : FRegistry : : Get ( ) . FragmentTypes )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
{
const uint32 ThisFragmentTypePriority = FragmentType . ComputePriority ( Object , Context ) ;
if ( ThisFragmentTypePriority > BestFragmentTypePriority )
{
BestFragmentTypePriority = ThisFragmentTypePriority ;
BestFragmentType = & FragmentType ;
}
}
if ( BestFragmentType & & BestFragmentType - > PayloadType ! = nullptr )
{
return BestFragmentType ;
}
return nullptr ;
}
FFragmentType * FFragmentTypeHandle : : Resolve ( ) const
{
2023-11-22 12:02:21 -05:00
return Handle = = 0xff ? nullptr : & FRegistry : : Get ( ) . FragmentTypes [ Handle ] ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
}
FFragmentTypeHandle MakeFragmentTypeHandle ( const FFragmentType * FragmentType )
{
check ( FragmentType ) ;
2023-11-22 12:02:21 -05:00
const uint64 FragmentTypeOffset = static_cast < uint64 > ( FragmentType - FRegistry : : Get ( ) . FragmentTypes . GetData ( ) ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
checkf ( FragmentTypeOffset < std : : numeric_limits < uint8 > : : max ( ) , TEXT ( " Maximum number of UOL FragmentTypes reached " ) ) ;
return FFragmentTypeHandle ( static_cast < uint8 > ( FragmentTypeOffset ) ) ;
}
} // UE::UniversalObjectLocator
Sequencer: Major integration of Universal Object Locators into Sequencer. This includes:
* Adding some first pass UI for editing the Universal Object Locators that are used to bind objects to tracks in Sequencer. This is currently in the form of:
* An 'Add Empty Binding' item in the + Actor to Sequencer menu
* A 'Binding Properties' sub-menu in the object binding track properties that allows you to modify the array of UOL's that bind objects to the track. This allows you to specifically select certain UOL types and then fill in the data that makes up that UOL. This is necessary for example for the FortAIPlayerPawn binding we plan on adding for UEFN.
* Major refactor of MovieScene binding code to more seamlessly allow for the use of the Locator resolution, especially in the case of supporting locators that spawn actors, either in the case of editor preview actors or future runtime 'spawnables'. This was necessary to consolidate the cases where locator resolve params were being created to as few places as possible for simplicity.
* Add the concept of 'default' editor and runtime flags to locators to make it easier for Sequencer to know how to interact with bindings of different types. These flags are then stored with the binding in Sequencer and editable by users as necessary. Sequencer treats 'load'/'unload' in this case similar to 'spawn'/'despawn' and generally speaking resolving a locator with editor flags of 'load' will expect the locator to spawn an actor for preview, while resolving a locator with runtime flags of 'load' will expect the locator to create a 'spawnable' character.
* To manage lifetime of these preview/spawnable characters, created an interface for a 'LocatorSpawnedCache' which the locators interact with when creating or destroying an actor. This allows the resolver of locators to differentiate between cases where they expect multiple resolves of the same locator to create duplicate actors vs. resolve to the same actor and track the lifetimes of these spawned actors.
* Sequencer implements the above cache within its own object cache in evaluation state.
* Expand the role of the previously created Binding Lifetime track to trigger loading/unloading via locators in the cache. The Binding Lifetime track will now automatically be added to a binding if its locator type contains 'Load' as a default editor or runtime flag.
* Some other editor updates to support the above.
Future issues to be resolved:
* We want to more easily expose the editing of binding properties when necessary rather than having it buried in a sub-menu. Ideas for this include some kind of UI on the track itself, for example a combo box with binding types on it, as well as potentially the properties menu appearing on hovering over the possessable icon on the object binding track. We also want to expose properties based on selection, for example object binding properties, track properties, and section properties, in another external window.
* We want to eventually replace the 'Actor to Sequencer' and 'Assign Actor' menu with this once the UX is better.
The above was all tested with a Fortnite NPC Locator type which will be added in a separate changelist.
[REVIEW] [at]ue-sequencer
#jira UE-199299
[CL 30596833 by david bromberg in ue5-main branch]
2024-01-12 12:41:00 -05:00
FUniversalObjectLocatorFragment : : FUniversalObjectLocatorFragment ( const UObject * InObject , UObject * Context )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
: bIsInitialized ( 0 )
, bIsInline ( 0 )
{
Reset ( InObject , Context ) ;
}
FUniversalObjectLocatorFragment : : FUniversalObjectLocatorFragment ( const UE : : UniversalObjectLocator : : FFragmentType & InFragmentType )
: FragmentType ( MakeFragmentTypeHandle ( & InFragmentType ) )
, bIsInitialized ( 0 )
, bIsInline ( 0 )
{
this - > InitializePayload ( InFragmentType . PayloadType ) ;
}
FUniversalObjectLocatorFragment : : FUniversalObjectLocatorFragment ( )
: bIsInitialized ( 0 )
, bIsInline ( 0 )
{
static_assert ( sizeof ( FUniversalObjectLocatorFragment ) = = FUniversalObjectLocatorFragment : : Size , " Unexpected size for FUniversalObjectLocatorFragment " ) ;
static_assert ( offsetof ( FUniversalObjectLocatorFragment , Data ) = = 0 , " FUniversalObjectLocatorFragment inline data is not aligned properly " ) ;
}
FUniversalObjectLocatorFragment : : ~ FUniversalObjectLocatorFragment ( )
{
if ( bIsInitialized & & ! IsEngineExitRequested ( ) )
{
DestroyPayload ( ) ;
}
}
FUniversalObjectLocatorFragment : : FUniversalObjectLocatorFragment ( const FUniversalObjectLocatorFragment & RHS )
: FragmentType ( RHS . FragmentType )
, bIsInitialized ( 0 )
, bIsInline ( 0 )
{
using namespace UE : : UniversalObjectLocator ;
if ( RHS . bIsInitialized )
{
const UScriptStruct * FragmentStruct = GetFragmentStruct ( ) ;
check ( FragmentStruct ) ;
this - > InitializePayload ( FragmentStruct ) ;
FragmentStruct - > CopyScriptStruct ( this - > GetPayload ( ) , RHS . GetPayload ( ) ) ;
}
else
{
this - > bIsInitialized = false ;
}
}
FUniversalObjectLocatorFragment & FUniversalObjectLocatorFragment : : operator = ( const FUniversalObjectLocatorFragment & RHS )
{
using namespace UE : : UniversalObjectLocator ;
this - > DestroyPayload ( ) ;
if ( RHS . bIsInitialized )
{
const UScriptStruct * FragmentStruct = GetFragmentStruct ( ) ;
// Assign the FragmentType and copy the payload
this - > FragmentType = RHS . FragmentType ;
this - > InitializePayload ( FragmentStruct ) ;
FragmentStruct - > CopyScriptStruct ( this - > GetPayload ( ) , RHS . GetPayload ( ) ) ;
}
else
{
this - > bIsInitialized = false ;
this - > FragmentType = FFragmentTypeHandle ( ) ;
}
return * this ;
}
FUniversalObjectLocatorFragment : : FUniversalObjectLocatorFragment ( FUniversalObjectLocatorFragment & & RHS )
: FragmentType ( RHS . FragmentType )
, bIsInitialized ( RHS . bIsInitialized )
, bIsInline ( RHS . bIsInline )
{
using namespace UE : : UniversalObjectLocator ;
FMemory : : Memcpy ( this - > Data , RHS . Data , sizeof ( Data ) ) ;
RHS . bIsInitialized = false ;
RHS . bIsInline = false ;
RHS . FragmentType = FFragmentTypeHandle ( ) ;
}
FUniversalObjectLocatorFragment & FUniversalObjectLocatorFragment : : operator = ( FUniversalObjectLocatorFragment & & RHS )
{
using namespace UE : : UniversalObjectLocator ;
this - > DestroyPayload ( ) ;
this - > bIsInitialized = RHS . bIsInitialized ;
this - > bIsInline = RHS . bIsInline ;
this - > FragmentType = RHS . FragmentType ;
FMemory : : Memcpy ( this - > Data , RHS . Data , sizeof ( Data ) ) ;
RHS . bIsInitialized = false ;
RHS . bIsInline = false ;
RHS . FragmentType = FFragmentTypeHandle ( ) ;
return * this ;
}
bool operator = = ( const FUniversalObjectLocatorFragment & A , const FUniversalObjectLocatorFragment & B )
{
using namespace UE : : UniversalObjectLocator ;
if ( A . bIsInitialized ! = B . bIsInitialized )
{
return false ;
}
else if ( ! A . bIsInitialized )
{
// 2 uninitialized references are the same
return true ;
}
else if ( A . FragmentType ! = B . FragmentType )
{
// Different fragment types
return false ;
}
else
{
const UScriptStruct * FragmentStruct = A . GetFragmentStruct ( ) ;
check ( FragmentStruct ) ;
// Same fragment types - compare payloads
const void * PayloadA = A . GetPayload ( ) ;
const void * PayloadB = B . GetPayload ( ) ;
return FragmentStruct - > CompareScriptStruct ( PayloadA , PayloadB , 0 ) ;
}
}
bool operator ! = ( const FUniversalObjectLocatorFragment & A , const FUniversalObjectLocatorFragment & B )
{
return ! ( A = = B ) ;
}
uint32 GetTypeHash ( const FUniversalObjectLocatorFragment & Fragment )
{
using namespace UE : : UniversalObjectLocator ;
if ( ! Fragment . bIsInitialized )
{
return 0 ;
}
uint32 Hash = GetTypeHash ( Fragment . FragmentType ) ;
if ( const FFragmentType * FragmentTypePtr = Fragment . GetFragmentType ( ) )
{
UScriptStruct * Struct = FragmentTypePtr - > GetStruct ( ) ;
if ( Struct )
{
const uint32 PayloadHash = Struct - > GetStructTypeHash ( Fragment . GetPayload ( ) ) ;
Hash = HashCombineFast ( Hash , PayloadHash ) ;
}
}
return Hash ;
}
# if DO_CHECK
void FUniversalObjectLocatorFragment : : CheckPayloadType ( UScriptStruct * TypeToCompare ) const
{
using namespace UE : : UniversalObjectLocator ;
const FFragmentType * FragmentTypePtr = GetFragmentType ( ) ;
checkf ( FragmentTypePtr = = nullptr | | FragmentTypePtr - > PayloadType = = TypeToCompare ,
TEXT ( " Type mismatch when accessing payload data! Attempting to access a stored %s payload as %s. " ) ,
FragmentTypePtr - > PayloadType ? * FragmentTypePtr - > PayloadType - > GetName ( ) : TEXT ( " <expired> " ) ,
TypeToCompare ? * TypeToCompare - > GetName ( ) : TEXT ( " <nullptr> " ) ) ;
}
# endif
void FUniversalObjectLocatorFragment : : ToString ( FStringBuilderBase & OutString ) const
{
using namespace UE : : UniversalObjectLocator ;
const FFragmentType * FragmentTypePtr = FragmentType . Resolve ( ) ;
if ( FragmentTypePtr & & FragmentTypePtr - > PayloadType )
{
FragmentTypePtr - > FragmentTypeID . AppendString ( OutString ) ;
TStringBuilder < 128 > PayloadString ;
FragmentTypePtr - > ToString ( GetPayload ( ) , PayloadString ) ;
if ( PayloadString . Len ( ) ! = 0 )
{
OutString + = ' = ' ;
OutString . Append ( PayloadString . ToView ( ) ) ;
}
}
}
UE : : UniversalObjectLocator : : FParseStringResult FUniversalObjectLocatorFragment : : TryParseString ( FStringView InString , const FParseStringParams & InParams )
{
using namespace UE : : UniversalObjectLocator ;
if ( InString . Len ( ) = = 0 )
{
Reset ( ) ;
return FParseStringResult ( ) . Success ( ) ;
}
// Check for a literal "none" text
static constexpr FStringView NoneString = TEXTVIEW ( " none " ) ;
if ( InString . Compare ( NoneString , ESearchCase : : IgnoreCase ) = = 0 )
{
Reset ( ) ;
return FParseStringResult ( ) . Success ( NoneString . Len ( ) ) ;
}
FStringView FragmentTypeString = InString ;
FStringView FragmentPayloadString ;
const int32 Delimiter = UE : : String : : FindFirstChar ( InString , ' = ' ) ;
if ( Delimiter ! = INDEX_NONE )
{
// We have a payload
FragmentTypeString = InString . Left ( Delimiter ) ;
FragmentPayloadString = InString . RightChop ( Delimiter + 1 ) ;
if ( FragmentTypeString . Len ( ) = = 0 )
{
return FParseStringResult ( ) . Failure ( UE_UOL_PARSE_ERROR ( InParams , LOCTEXT ( " Error_UnexpectedEquals " , " Unexpected '='' when expecting a fragment type. " ) ) ) ;
}
}
FParseStringResult TypeResult = TryParseFragmentType ( FragmentTypeString , InParams ) ;
if ( ! TypeResult )
{
return TypeResult ;
}
FParseStringResult PayloadResult = TryParseFragmentPayload ( FragmentPayloadString , InParams ) ;
// Add the type chars and = to the total num parsed
PayloadResult . NumCharsParsed + = TypeResult . NumCharsParsed + 1 ;
return PayloadResult ;
}
UE : : UniversalObjectLocator : : FParseStringResult FUniversalObjectLocatorFragment : : TryParseFragmentType ( FStringView InString , const FParseStringParams & InParams )
{
using namespace UE : : UniversalObjectLocator ;
if ( InString . Len ( ) = = 0 )
{
return FParseStringResult ( ) . Failure ( UE_UOL_PARSE_ERROR ( InParams , LOCTEXT ( " Error_EmptyFragmentType " , " Fragment type specifier is empty. " ) ) ) ;
}
// Check for a literal "none" text
static constexpr FStringView NoneString = TEXTVIEW ( " none " ) ;
if ( InString . Compare ( NoneString , ESearchCase : : IgnoreCase ) = = 0 )
{
Reset ( ) ;
return FParseStringResult ( ) . Success ( NoneString . Len ( ) ) ;
}
// Try and find the FragmentType as a name
FName FragmentTypeID ( InString . Len ( ) , InString . GetData ( ) , FNAME_Find ) ;
if ( FragmentTypeID ! = NAME_None )
{
// Find the FragmentType
2023-11-22 12:02:21 -05:00
const FFragmentType * SerializedFragmentType = FRegistry : : Get ( ) . FindFragmentType ( FragmentTypeID ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
if ( SerializedFragmentType ! = nullptr & & SerializedFragmentType - > PayloadType ! = nullptr )
{
this - > DestroyPayload ( ) ;
this - > FragmentType = MakeFragmentTypeHandle ( SerializedFragmentType ) ;
this - > InitializePayload ( SerializedFragmentType - > PayloadType ) ;
return FParseStringResult ( ) . Success ( InString . Len ( ) ) ;
}
}
// Not a valid fragment type string
return FParseStringResult ( ) . Failure (
UE_UOL_PARSE_ERROR ( InParams ,
FText : : Format (
LOCTEXT ( " Error_UnknownFragmentType " , " Unknown fragment type specifier {0}. " ) ,
FText : : FromStringView ( InString )
)
)
) ;
}
UE : : UniversalObjectLocator : : FParseStringResult FUniversalObjectLocatorFragment : : TryParseFragmentPayload ( FStringView InString , const FParseStringParams & InParams )
{
using namespace UE : : UniversalObjectLocator ;
if ( ! bIsInitialized )
{
return FParseStringResult ( ) . Failure ( UE_UOL_PARSE_ERROR ( InParams , LOCTEXT ( " Error_Uninitialized " , " Unable to parse a payload for an uninitialized fragment. " ) ) ) ;
}
const FFragmentType * FragmentTypePtr = GetFragmentType ( ) ;
const UScriptStruct * FragmentStruct = FragmentTypePtr ? FragmentTypePtr - > GetStruct ( ) : nullptr ;
if ( ! FragmentStruct )
{
return FParseStringResult ( ) . Failure ( UE_UOL_PARSE_ERROR ( InParams , LOCTEXT ( " Error_Expired " , " Unable to parse a payload for a fragment whose type has expired. " ) ) ) ;
}
void * Payload = GetPayload ( ) ;
if ( InString . Len ( ) = = 0 )
{
// Empty payload string means a default payload
FragmentStruct - > ClearScriptStruct ( Payload ) ;
return FParseStringResult ( ) . Success ( ) ;
}
return FragmentTypePtr - > TryParseString ( Payload , InString , InParams ) ;
}
const UE : : UniversalObjectLocator : : FFragmentType * FUniversalObjectLocatorFragment : : GetFragmentType ( ) const
{
return FragmentType . Resolve ( ) ;
}
UScriptStruct * FUniversalObjectLocatorFragment : : GetFragmentStruct ( ) const
{
using namespace UE : : UniversalObjectLocator ;
if ( const FFragmentType * Type = GetFragmentType ( ) )
{
return Type - > GetStruct ( ) ;
}
return nullptr ;
}
UE : : UniversalObjectLocator : : FFragmentTypeHandle FUniversalObjectLocatorFragment : : GetFragmentTypeHandle ( ) const
{
using namespace UE : : UniversalObjectLocator ;
const FFragmentType * FragmentTypePtr = GetFragmentType ( ) ;
if ( FragmentTypePtr )
{
return MakeFragmentTypeHandle ( FragmentTypePtr ) ;
}
return FFragmentTypeHandle ( ) ;
}
void FUniversalObjectLocatorFragment : : DestroyPayload ( )
{
using namespace UE : : UniversalObjectLocator ;
if ( ! bIsInitialized )
{
return ;
}
void * Payload = GetPayload ( ) ;
const UScriptStruct * FragmentStruct = GetFragmentStruct ( ) ;
if ( ensureMsgf ( FragmentStruct , TEXT ( " FUniversalObjectLocatorFragment has outlived its FragmentType's payload type struct! This could leak memory if the type allocated it. " ) ) )
{
FragmentStruct - > DestroyStruct ( Payload ) ;
}
if ( ! bIsInline )
{
FMemory : : Free ( Payload ) ;
}
bIsInitialized = false ;
}
void * FUniversalObjectLocatorFragment : : GetPayload ( )
{
check ( bIsInitialized ) ;
return bIsInline ? Data : * ( ( void * * ) Data ) ;
}
const void * FUniversalObjectLocatorFragment : : GetPayload ( ) const
{
return bIsInline ? Data : * ( ( const void * const * ) Data ) ;
}
void FUniversalObjectLocatorFragment : : InitializePayload ( const UScriptStruct * PayloadType )
{
check ( ! bIsInitialized ) ;
bIsInitialized = true ;
if ( PayloadType - > GetStructureSize ( ) < = sizeof ( FUniversalObjectLocatorFragment : : Data ) & & PayloadType - > GetMinAlignment ( ) < = alignof ( FUniversalObjectLocatorFragment ) )
{
// We can placement new this into the payload data
bIsInline = true ;
}
else
{
// We have to allocate this struct on the heap
void * HeapAllocation = FMemory : : Malloc ( PayloadType - > GetStructureSize ( ) , PayloadType - > GetMinAlignment ( ) ) ;
* reinterpret_cast < void * * > ( Data ) = HeapAllocation ;
bIsInline = false ;
}
PayloadType - > InitializeStruct ( GetPayload ( ) ) ;
}
void FUniversalObjectLocatorFragment : : Reset ( )
{
using namespace UE : : UniversalObjectLocator ;
DestroyPayload ( ) ;
FragmentType = FFragmentTypeHandle ( ) ;
}
Sequencer: Major integration of Universal Object Locators into Sequencer. This includes:
* Adding some first pass UI for editing the Universal Object Locators that are used to bind objects to tracks in Sequencer. This is currently in the form of:
* An 'Add Empty Binding' item in the + Actor to Sequencer menu
* A 'Binding Properties' sub-menu in the object binding track properties that allows you to modify the array of UOL's that bind objects to the track. This allows you to specifically select certain UOL types and then fill in the data that makes up that UOL. This is necessary for example for the FortAIPlayerPawn binding we plan on adding for UEFN.
* Major refactor of MovieScene binding code to more seamlessly allow for the use of the Locator resolution, especially in the case of supporting locators that spawn actors, either in the case of editor preview actors or future runtime 'spawnables'. This was necessary to consolidate the cases where locator resolve params were being created to as few places as possible for simplicity.
* Add the concept of 'default' editor and runtime flags to locators to make it easier for Sequencer to know how to interact with bindings of different types. These flags are then stored with the binding in Sequencer and editable by users as necessary. Sequencer treats 'load'/'unload' in this case similar to 'spawn'/'despawn' and generally speaking resolving a locator with editor flags of 'load' will expect the locator to spawn an actor for preview, while resolving a locator with runtime flags of 'load' will expect the locator to create a 'spawnable' character.
* To manage lifetime of these preview/spawnable characters, created an interface for a 'LocatorSpawnedCache' which the locators interact with when creating or destroying an actor. This allows the resolver of locators to differentiate between cases where they expect multiple resolves of the same locator to create duplicate actors vs. resolve to the same actor and track the lifetimes of these spawned actors.
* Sequencer implements the above cache within its own object cache in evaluation state.
* Expand the role of the previously created Binding Lifetime track to trigger loading/unloading via locators in the cache. The Binding Lifetime track will now automatically be added to a binding if its locator type contains 'Load' as a default editor or runtime flag.
* Some other editor updates to support the above.
Future issues to be resolved:
* We want to more easily expose the editing of binding properties when necessary rather than having it buried in a sub-menu. Ideas for this include some kind of UI on the track itself, for example a combo box with binding types on it, as well as potentially the properties menu appearing on hovering over the possessable icon on the object binding track. We also want to expose properties based on selection, for example object binding properties, track properties, and section properties, in another external window.
* We want to eventually replace the 'Actor to Sequencer' and 'Assign Actor' menu with this once the UX is better.
The above was all tested with a Fortnite NPC Locator type which will be added in a separate changelist.
[REVIEW] [at]ue-sequencer
#jira UE-199299
[CL 30596833 by david bromberg in ue5-main branch]
2024-01-12 12:41:00 -05:00
void FUniversalObjectLocatorFragment : : Reset ( const UObject * InObject , UObject * Context )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
{
using namespace UE : : UniversalObjectLocator ;
Reset ( ) ;
if ( const FFragmentType * BestFragmentType = FindBestFragmentType ( InObject , Context ) )
{
FragmentType = MakeFragmentTypeHandle ( BestFragmentType ) ;
InitializePayload ( BestFragmentType - > PayloadType ) ;
2023-11-14 12:00:43 -05:00
BestFragmentType - > InitializePayload ( GetPayload ( ) , FInitializeParams { InObject , Context } ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
}
}
Sequencer: Major integration of Universal Object Locators into Sequencer. This includes:
* Adding some first pass UI for editing the Universal Object Locators that are used to bind objects to tracks in Sequencer. This is currently in the form of:
* An 'Add Empty Binding' item in the + Actor to Sequencer menu
* A 'Binding Properties' sub-menu in the object binding track properties that allows you to modify the array of UOL's that bind objects to the track. This allows you to specifically select certain UOL types and then fill in the data that makes up that UOL. This is necessary for example for the FortAIPlayerPawn binding we plan on adding for UEFN.
* Major refactor of MovieScene binding code to more seamlessly allow for the use of the Locator resolution, especially in the case of supporting locators that spawn actors, either in the case of editor preview actors or future runtime 'spawnables'. This was necessary to consolidate the cases where locator resolve params were being created to as few places as possible for simplicity.
* Add the concept of 'default' editor and runtime flags to locators to make it easier for Sequencer to know how to interact with bindings of different types. These flags are then stored with the binding in Sequencer and editable by users as necessary. Sequencer treats 'load'/'unload' in this case similar to 'spawn'/'despawn' and generally speaking resolving a locator with editor flags of 'load' will expect the locator to spawn an actor for preview, while resolving a locator with runtime flags of 'load' will expect the locator to create a 'spawnable' character.
* To manage lifetime of these preview/spawnable characters, created an interface for a 'LocatorSpawnedCache' which the locators interact with when creating or destroying an actor. This allows the resolver of locators to differentiate between cases where they expect multiple resolves of the same locator to create duplicate actors vs. resolve to the same actor and track the lifetimes of these spawned actors.
* Sequencer implements the above cache within its own object cache in evaluation state.
* Expand the role of the previously created Binding Lifetime track to trigger loading/unloading via locators in the cache. The Binding Lifetime track will now automatically be added to a binding if its locator type contains 'Load' as a default editor or runtime flag.
* Some other editor updates to support the above.
Future issues to be resolved:
* We want to more easily expose the editing of binding properties when necessary rather than having it buried in a sub-menu. Ideas for this include some kind of UI on the track itself, for example a combo box with binding types on it, as well as potentially the properties menu appearing on hovering over the possessable icon on the object binding track. We also want to expose properties based on selection, for example object binding properties, track properties, and section properties, in another external window.
* We want to eventually replace the 'Actor to Sequencer' and 'Assign Actor' menu with this once the UX is better.
The above was all tested with a Fortnite NPC Locator type which will be added in a separate changelist.
[REVIEW] [at]ue-sequencer
#jira UE-199299
[CL 30596833 by david bromberg in ue5-main branch]
2024-01-12 12:41:00 -05:00
void FUniversalObjectLocatorFragment : : Reset ( const UObject * InObject , UObject * Context , TFunctionRef < bool ( UE : : UniversalObjectLocator : : FFragmentTypeHandle ) > CanUseFragmentType )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
{
using namespace UE : : UniversalObjectLocator ;
Reset ( ) ;
// Loop through all our FragmentTypes to find the most supported one
uint32 BestFragmentTypePriority = 0 ;
const FFragmentType * BestFragmentType = nullptr ;
2023-11-22 12:02:21 -05:00
TArray < FFragmentType > & FragmentTypes = FRegistry : : Get ( ) . FragmentTypes ;
if ( ! ensure ( FragmentTypes . Num ( ) < 255 ) )
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
{
return ;
}
2023-11-22 12:02:21 -05:00
const uint8 Num = static_cast < uint8 > ( FragmentTypes . Num ( ) ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
for ( uint8 Index = 0 ; Index < Num ; + + Index )
{
if ( ! CanUseFragmentType ( FFragmentTypeHandle ( Index ) ) )
{
continue ;
}
2023-11-22 12:02:21 -05:00
const FFragmentType & ThisFragmentType = FragmentTypes [ Index ] ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
const uint32 ThisFragmentTypePriority = ThisFragmentType . ComputePriority ( InObject , Context ) ;
if ( ThisFragmentTypePriority > BestFragmentTypePriority )
{
BestFragmentTypePriority = ThisFragmentTypePriority ;
BestFragmentType = & ThisFragmentType ;
}
}
if ( BestFragmentType & & BestFragmentType - > PayloadType ! = nullptr )
{
FragmentType = MakeFragmentTypeHandle ( BestFragmentType ) ;
InitializePayload ( BestFragmentType - > PayloadType ) ;
2023-11-14 12:00:43 -05:00
BestFragmentType - > InitializePayload ( GetPayload ( ) , FInitializeParams { InObject , Context } ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
}
}
UE : : UniversalObjectLocator : : FResolveResult FUniversalObjectLocatorFragment : : Resolve ( const UE : : UniversalObjectLocator : : FResolveParams & Params ) const
{
using namespace UE : : UniversalObjectLocator ;
// Find our FragmentType
const FFragmentType * FragmentTypePtr = FragmentType . Resolve ( ) ;
if ( FragmentTypePtr & & bIsInitialized )
{
return FragmentTypePtr - > ResolvePayload ( GetPayload ( ) , Params ) ;
}
return FResolveResult ( ) ;
}
bool FUniversalObjectLocatorFragment : : Serialize ( FArchive & Ar )
{
using namespace UE : : UniversalObjectLocator ;
if ( Ar . IsLoading ( ) )
{
FName FragmentTypeID ;
Ar < < FragmentTypeID ;
if ( FragmentTypeID = = NAME_None )
{
Reset ( ) ;
}
else
{
// Find the FragmentType
2023-11-22 12:02:21 -05:00
const FFragmentType * SerializedFragmentType = FRegistry : : Get ( ) . FindFragmentType ( FragmentTypeID ) ;
Added initial draft of Universal Object Locator mechanism
Universal Object Locators (UOLs) are designed to support referencing objects that don't fit neatly into a basic outer->inner path representation. Examples might include transient actors, dynamically created objects, or objects that need to be referenced by an external ID or using external lookup logic. Specifically this might be an object spawned by Sequencer, a transient object on a USD stage, or a gameplay-specific object created by a game system.
A UOL comprises zero or more 'fragments': atomic pieces of data and logic that defines how to lookup or load an object based on a context. Fragment types are globally registered as part of module initialization.
UOLs are hashable, and support string conversion that conforms to RFC3986 so they can be used as URIs (though that is not a current use-case). In order to support this type of string conversion, the 'path' part of of a UOL defines the fragment types, and the query string is used to encode the payload data for each fragment. This allows us to support a more diverse set of characters as part of payload strings (ie, / : and .) which are otherwise unsupported as part of the path.
An example UOL to an anim instance might look like: uobj://actor/subobj/animinst?payload0=/Path/To/Package.LevelName:PathToActor&payload1=ComponentName
#rb david.bromberg, ludovic.chabant, Max.Chen
[CL 29714989 by andrew rodham in ue5-main branch]
2023-11-14 11:31:58 -05:00
if ( ! SerializedFragmentType | | ! SerializedFragmentType - > PayloadType )
{
Reset ( ) ;
// Big error - what do we do?
UE_LOG ( LogUniversalObjectLocator , Error , TEXT ( " WARNING: POTENTIAL DATA LOSS! Universal Object Reference FragmentType %s is not recognized! This reference will be lost if re-saved. " ) , * FragmentTypeID . ToString ( ) ) ;
checkf ( false , TEXT ( " WARNING: POTENTIAL DATA LOSS! Universal Object Reference FragmentType %s! This reference will be lost if re-saved. " ) , * FragmentTypeID . ToString ( ) ) ;
// Deserialize an empty payload so we don't corrupt the serialization data
FUniversalObjectLocatorEmptyPayload Empty ;
FUniversalObjectLocatorEmptyPayload : : StaticStruct ( ) - > SerializeItem ( Ar , & Empty , nullptr ) ;
return true ;
}
FragmentType = MakeFragmentTypeHandle ( SerializedFragmentType ) ;
UScriptStruct * Struct = SerializedFragmentType - > GetStruct ( ) ;
Ar . Preload ( Struct ) ;
InitializePayload ( Struct ) ;
Struct - > SerializeItem ( Ar , GetPayload ( ) , nullptr ) ;
}
}
else if ( Ar . IsSaving ( ) | | Ar . IsTransacting ( ) )
{
const FFragmentType * FragmentTypePtr = FragmentType . Resolve ( ) ;
if ( FragmentTypePtr = = nullptr )
{
FName None ;
// FragmentType ID
Ar < < None ;
}
else
{
FName FragmentTypeID = FragmentTypePtr - > FragmentTypeID ;
// FragmentType ID
Ar < < FragmentTypeID ;
// FragmentType payload
FragmentTypePtr - > GetStruct ( ) - > SerializeItem ( Ar , GetPayload ( ) , nullptr ) ;
}
}
else if ( Ar . IsModifyingWeakAndStrongReferences ( ) )
{
UScriptStruct * FragmentStruct = GetFragmentStruct ( ) ;
if ( FragmentStruct & & bIsInitialized )
{
FragmentStruct - > SerializeItem ( Ar , GetPayload ( ) , nullptr ) ;
}
}
return true ;
}
void FUniversalObjectLocatorFragment : : AddStructReferencedObjects ( FReferenceCollector & Collector )
{
using namespace UE : : UniversalObjectLocator ;
if ( FFragmentType * FragmentTypePtr = FragmentType . Resolve ( ) )
{
Collector . AddReferencedObject ( FragmentTypePtr - > PayloadType ) ;
if ( bIsInitialized & & FragmentTypePtr - > PayloadType )
{
Collector . AddReferencedObjects ( FragmentTypePtr - > PayloadType , GetPayload ( ) ) ;
}
}
}
bool FUniversalObjectLocatorFragment : : ExportTextItem ( FString & ValueStr , const FUniversalObjectLocatorFragment & DefaultValue , UObject * Parent , int32 PortFlags , UObject * ExportRootScope ) const
{
TStringBuilder < 32 > PayloadString ;
ToString ( PayloadString ) ;
ValueStr . AppendChar ( ' ( ' ) ;
ValueStr . Append ( PayloadString . ToString ( ) , PayloadString . Len ( ) ) ;
ValueStr . AppendChar ( ' ) ' ) ;
return true ;
}
bool FUniversalObjectLocatorFragment : : ImportTextItem ( const TCHAR * & Buffer , int32 PortFlags , UObject * Parent , FOutputDevice * ErrorText , FArchive * InSerializingArchive )
{
using namespace UE : : UniversalObjectLocator ;
if ( Buffer & & * Buffer = = ' ( ' )
{
const TCHAR * BufferEnd = FCString : : Strchr ( Buffer , ' ) ' ) ;
if ( Buffer ! = BufferEnd & & ( BufferEnd - Buffer ) < std : : numeric_limits < int32 > : : max ( ) )
{
FStringView View ( Buffer + 1 , int32 ( BufferEnd - Buffer ) - 1 ) ;
if ( TryParseString ( View , FParseStringParams ( ) ) )
{
return true ;
}
}
}
return false ;
}
bool FUniversalObjectLocatorFragment : : SerializeFromMismatchedTag ( const FPropertyTag & Tag , FStructuredArchive : : FSlot Slot )
{
return false ;
}
void FUniversalObjectLocatorFragment : : GetPreloadDependencies ( TArray < UObject * > & OutDeps )
{
}
# undef LOCTEXT_NAMESPACE