Merge pull request #1489 from aws-lumberyard-dev/mbalfour/gitflow_210622

Integration from stabilization/2106 at commit d5490ecf69 .
This commit is contained in:
Mike Balfour
2021-06-22 15:14:22 -05:00
committed by GitHub
184 changed files with 17071 additions and 1103 deletions
@@ -57,7 +57,13 @@ namespace AZ
//! Determines if a component can be created from the asset type
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
//! You can use this to filter by subIds or do your own validation here if needed
virtual bool CanCreateComponent(const AZ::Data::AssetId& /*assetId*/) const { return true; }
virtual bool CanCreateComponent([[maybe_unused]] const AZ::Data::AssetId& assetId) const { return true; }
//! Determines if other products conflict with the given one when multiple are generated from a source asset.
//! This will be called before attempting to create a component from an asset (drag&drop, etc)
//! You can use this to filter by conflicting product types or in case you want to skip for UX reasons.
//! @param[in] productAssetTypes Asset types of all generated products, including the one for our given type in this bus.
virtual bool HasConflictingProducts([[maybe_unused]] const AZStd::vector<AZ::Data::AssetType>& productAssetTypes) const { return false; }
};
using AssetTypeInfoBus = AZ::EBus<AssetTypeInfo>;
@@ -1206,7 +1206,7 @@ namespace JsonSerializationTests
if (this->m_features.m_enableInitializationTest)
{
auto instance = this->m_description.CreateDefaultInstance();
typename TypeParam::Type compare;
AZStd::remove_cvref_t<typename TypeParam::Type> compare;
if (!this->m_description.AreEqual(*instance, compare))
{
auto serializer = this->m_description.CreateSerializer();
@@ -75,6 +75,8 @@ namespace AzFramework
// Add a fullscreen button in the upper right of the title bar.
[m_nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
m_nativeWindow.tabbingMode = NSWindowTabbingModeDisallowed;
// Make the window active
[m_nativeWindow makeKeyAndOrderFront:nil];
m_nativeWindow.title = m_windowTitle;
@@ -44,6 +44,11 @@ namespace AzNetworking
//! @class IConnection
//! @brief interface class for network connections.
//!
//! IConnection provides a pure-virtual interface for all network connection types. IConnections provide access to
//! a ConnectionMetrics object which provides a variety of metrics on the connection itself such as data rate, RTT and
//! packet statistics.
class IConnection
{
public:
@@ -22,6 +22,12 @@ namespace AzNetworking
{
//! @class IConnectionListener
//! @brief interface class for application layer dealing with connection level events.
//!
//! IConnectionListener defines an abstract interface that the user of AzNetworking is expected to implement to react and
//! handle all IConnection related events, including the handling of any received IPacket derived packets. The AzNetworking
//! user should derive a handler class from IConnectionListener, and provide an instance of that handler to any
//! INetworkInterface the user instantiates. The lifetime of the IConnectionListener must outlive the lifetime of the
//! INetworkInterface.
class IConnectionListener
{
public:
@@ -18,6 +18,11 @@ namespace AzNetworking
{
//! @class IConnectionSet
//! @brief interface class for managing a set of connections.
//!
//! IConnectionSet defines a simple interface for working with an abstract set of IConnections bound to an
//! INetworkInterface. Generally users of AzNetworking will not have reason to interact directly with the IConnectionSet,
//! as its interface is completely wrapped by INetworkInterface.
class IConnectionSet
{
public:
@@ -23,10 +23,10 @@ namespace AzNetworking
//! Collection of compression related error codes
enum class CompressorError
{
Ok, ///< No error, operation finished successfully
InsufficientBuffer, ///< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, ///< Malformed or hacked packet, potentially security issue
Uninitialized ///< Compressor or supplied buffers are uninitialized
Ok, //!< No error, operation finished successfully
InsufficientBuffer, //!< Buffer size is insufficient for the operation to complete, increase the size and try again
CorruptData, //!< Malformed or hacked packet, potentially security issue
Uninitialized //!< Compressor or supplied buffers are uninitialized
};
//! Unique identifier of a given compressor
@@ -34,6 +34,12 @@ namespace AzNetworking
//! @class ICompressor
//! @brief Packet data compressor interface.
//!
//! ICompressor is an abstract compression interface meant for user provided GEMs to implement (such as the [Multiplayer
//! Compression Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression)).
//! Compression is supported for both TCP and UDP connections. Instantiation of a compressor is controlled by the
//! `net_UdpCompressor` or `net_TcpCompressor` cvar for their respective protocols.
class ICompressor
{
public:
@@ -87,8 +93,16 @@ namespace AzNetworking
) = 0;
};
//! Abstract factory to instantiate compressors.
//! Used by the network interface to create a compressor
//! @class ICompressorFactory
//! @brief Abstract factory to instantiate compressors.
//!
//! ICompressorFactory is an abstract compression interface meant for user provided GEMs to implement. ICompressorFactory
//! implementations can be registered to classes implementing INetworking. Registered factories can then be used to create
//! ICompressor implementations on demand. The [Multiplayer Compression
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of an ICompressorFactory
//! for an LZ4 Compressor. In it, MultiplayerCompressionSystemComponent registers its ICompressorFactory with
//! NetworkingSystemComponent, which is an implementation of INetworking. Registered factories are keyed by their AZ Name
//! which is accessed through the factory's GetFactoryName method.
class ICompressorFactory
{
public:
@@ -22,7 +22,16 @@
namespace AzNetworking
{
//! @class INetworkInterface
//! @brief pure virtual network interface class to abstract client/server and tcp/udp concerns from application code.
//! @brief Network interface class to abstract client/server and protocol concerns from application code.
//!
//! INetworkInterface provides an abstract API capable of receiving and opening IConnection objects, sending IPacket objects with optional
//! reliability, and determining the delivery status of packets that have been sent unreliably (delivery of reliable packets
//! is guaranteed as long as the associated connection remains open). INetworkInterface must be provided an
//! IConnectionListener instance that outlives the INetworkInterface itself. The INetworkInterface also creates and manages
//! the IConnectionSet, which tracks all open connections bound to the interface. INetworkInterface also provides GetMetrics
//! functions which can be used to fetch a struct detailing a variety of metrics relating to send and receive rates for both
//! packets and bytes in addition to the effect of features on those rates (such as packet size reduction due to compression.)
class INetworkInterface
{
public:
@@ -23,6 +23,17 @@ namespace AzNetworking
//! @class INetworking
//! @brief The interface for creating and working with network interfaces.
//!
//! INetworking is an Az::Interface<T> that provides applications access to higher level networking abstractions.
//! AzNetworking::INetworking can be used to instantiate new INetworkInterfaces that can be configured to operate over
//! either TCP or UDP, enable or disable encryption, and be assigned a trust level.
//!
//! INetworking is also responsible for registering ICompressorFactory implementations. This allows a developer to have
//! access to multiple ICompressorFactory implementations by name. The [MultiplayerCompressor
//! Gem](http://docs.o3de.org/docs/user-guide/gems/reference/multiplayer-compression) is an example of this using the
//! [LZ4](https://wikipedia.org/wiki/LZ4_%28compression_algorithm%29) algorithm.
//!
class INetworking
{
public:
@@ -24,6 +24,15 @@ namespace AzNetworking
//! @class IPacket
//! @brief Base class for all packets.
//!
//! IPacket defines an abstract interface that all packets transmitted using AzNetworking must conform to. While there are
//! a number of core packets used internally by AzNetworking, it is fully possible for end-users to define their own custom
//! packets using this interface. PacketType should be distinct, and should be greater than
//! AzNetworking::CorePackets::MAX. The Serialize method allows the IPacket to be used by an
//! ISerializer to move data between hosts safely and efficiently.
//!
//! For more information on the packet format and best practices for extending the packet system, read
//! [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) on the O3DE documentation site.
class IPacket
{
public:
@@ -28,6 +28,19 @@ namespace AzNetworking
//! @class IPacketHeader
//! @brief A packet header that lets us deduce packet type for any incoming packet.
//!
//! IPacketHeader defines an abstract interface for a descriptor of all AzNetworking::IPacket sent through AzNetworking. The
//! PacketHeader is used to identify and describe the contents of a Packet so that transport logic can identify what
//! additional processing steps need to be taken (if any) and what type of Packet is being inspected.
//!
//! The PacketFlags portion of the header represents the first byte of the header. While it can be encrypted it is
//! otherwise not exposed to additional processing (such as an AzNetworking::ICompressor). PacketFlags are a bitfield use to provide up
//! front information about the state of the packet. Currently there is only one flag to indicate if the Packet is
//! compressed or not.
//!
//! The remainder of the header contains the PacketType and the PacketId. While the PacketFlags byte is exempt from most
//! additional forms of processing, the remainder of the header is not.
class IPacketHeader
{
public:
@@ -27,6 +27,18 @@ namespace AzNetworking
//! @class ISerializer
//! @brief Interface class for all serializers to derive from.
//!
//! ISerializer defines an abstract interface for visiting an object hierarchy and performing operations upon that hierarchy,
//! typically reading from or writing data to the object hierarchy for reasons of persistence or network transmission.
//!
//! While the most common types of serializers are provided by the AzNetworking framework, users can implement custom
//! serializers and perform complex operations on any serializable structures. A few types native to AzNetworking, many of which
//! relate to packets, demonstrate this.
//!
//! Provided serializers include NetworkInputSerializer for writing an object model into a bytestream, NetworkOutputSerializer
//! for writing to an object model, TrackChangesSerializer which is used to efficiently serialize objects without incurring significant
//! copy or comparison overhead, and HashSerializer which can be used to generate a hash of all visited data which is important for
//! automated desync detection.
class ISerializer
{
public:
@@ -25,6 +25,44 @@ namespace AzNetworking
//! @class TcpNetworkInterface
//! @brief This class implements a TCP network interface.
//!
//! TcpNetworkInterface is an implementation of AzNetworking::INetworkInterface.
//! Unlike UDP, TCP implements a variety of transport features such as congestion
//! avoidance, flow control, and reliability. These features are valuable, but TCP
//! offers minimal configuration of them. This is why UdpNetworkInterface offers
//! similar features, but with greater flexibility in configuration. If your project doesn't
//! require the low latency of UDP, consider using TCP.
//!
//! ## Packet structure
//!
//! * Flags - A bitfield a receiving endpoint can quickly inspect to learn about configuration of a packet
//! * Header - Details the type of packet and other information related to reliability
//! * Payload - The actual serialized content of the packet
//!
//! For more information, read [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) in the O3DE documentation.
//!
//! ## Reliability
//!
//! TCP packets can only be sent reliably. This is a feature of TCP itself.
//!
//! ## Fragmentation
//!
//! TCP implements fragmentation under the hood. Consumers of TCP packets will never
//! need to worry about reconstructing the contents over multiple transmissions.
//!
//! ## Compression
//!
//! Compression here refers to content insensitive compression using libraries like
//! LZ4. If enabled, the target payload is run through the compressor and replaces
//! the original payload if it's in fact smaller. To tell if compression is enabled
//! on a given packet, we operate on a bit in the packet's Flags. The Sender writes
//! this bit while the Receiver checks it to see if a packet needs to be
//! decompressed.
//!
//! ## Encryption
//!
//! AzNetworking uses the [OpenSSL](https://www.openssl.org/) library to implement TLS encryption. If enabled,
//! the O3DE network layer handles the OpenSSL handshake under the hood using provided certificates.
class TcpNetworkInterface final
: public INetworkInterface
{
@@ -27,12 +27,58 @@ namespace AzNetworking
class IConnectionListener;
class ICompressor;
// 20 byte IPv4 header + 8 byte UDP header
static const uint32_t UdpPacketHeaderSize = 20 + 8;
static const uint32_t DtlsPacketHeaderSize = 13; // DTLS1_RT_HEADER_LENGTH
static const uint32_t UdpPacketHeaderSize = 20 + 8; //!< 20 byte IPv4 header + 8 byte UDP header
static const uint32_t DtlsPacketHeaderSize = 13; //!< DTLS1_RT_HEADER_LENGTH
//! @class UdpNetworkInterface
//! @brief This class implements a UDP network interface.
//!
//! UdpNetworkInterface is an implementation of AzNetworking::INetworkInterface. Since UDP is a very bare bones protocol,
//! the Open 3D Engine implementation has to provide significantly more than its TCP counterpart (since TCP implements a
//! significant number of reliability features.)
//!
//! When sent through UDP, a packet can have additional actions performed on it depending on which features are enabled and
//! configured. Each feature listed in this description is in the order a packet will see them on Send.
//!
//! ### Packet structure
//!
//! The general structure of a UDP packet is:
//!
//! * Flags - A bitfield a receiving endpoint can quickly inspect to learn about configuration of a packet
//! * Header - Details the type of packet and other information related to reliability
//! * Payload - The actual serialized content of the packet
//!
//! For more information, read [Networking Packets](http://docs.o3de.org/docs/user-guide/networking/packets) in the O3DE documentation.
//!
//! ### Reliability
//!
//! UDP packets can be sent reliably or unreliably. Reliably sent packets are registered for tracking first. This causes the
//! reliable packet to be resent if a timeout on the packet is reached. Once the packet is acknowledged, the packet is
//! unregistered.
//!
//! ### Fragmentation
//!
//! If the raw packet size exceeds the configured maximum transmission unit (MTU) then the packet is broken into
//! multiple reliable fragments to avoid fragmentation at the routing level. Fragments are always reliable so the original
//! packet can be reconstructed. Operations that alter the payload generally follow this step so that they can be
//! separately applied to the Fragments in addition to not being applied to both the original and Fragments.
//!
//! ### Compression
//!
//! Compression here refers to content insensitive compression using libraries like LZ4. If enabled, the target payload is
//! run through the compressor and replaces the original payload if it's in fact smaller. To tell if compression is enabled
//! on a given packet, we operate on a bit in the packet's Flags. The Sender writes this bit while the Receiver checks it to
//! see if a packet needs to be decompressed.
//!
//! O3DE could potentially move from over MTU to under with compression, and the UDP interface doesn't check for this. Detecting a change
//! that would reduce the number of fragmented packets would require pre-emptively compressing payloads to tell if that change happened,
//! which could potentially lead to a lot of unnecessary calls to the compressor.
//!
//! ### Encryption
//!
//! AzNetworking uses the [OpenSSL](https://www.openssl.org/) library to implement Datagram Layer Transport Security (DTLS) encryption
//! on UDP traffic. Encryption operates as described in [O3DE Networking Encryption](http://docs.o3de.org/docs/user-guide/networking/encryption)
//! on the documentation website. Once both endpoints have completed their handshake, all traffic is expected to be fully encrypted.
class UdpNetworkInterface final
: public INetworkInterface
{
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Helpers Icon</title>
<defs>
<circle id="path-1" cx="8" cy="8" r="8"></circle>
<path d="M8.59736328,10.1821289 L8.59736328,9.79394531 C8.59736328,9.07617188 8.86835937,8.71728516 9.84248047,8.15332031 C10.8751953,7.54541016 11.4098633,6.77636719 11.4098633,5.67041016 C11.4098633,4.00048828 10.0255859,2.8359375 7.93085937,2.8359375 C5.68232422,2.8359375 4.40791016,4.09570312 4.37128906,5.89746094 L6.35615234,5.89746094 C6.40009766,5.06982422 6.95673828,4.53515625 7.79902344,4.53515625 C8.63398437,4.53515625 9.190625,5.04052734 9.190625,5.73632812 C9.190625,6.43212891 8.90498047,6.79101562 7.96015625,7.35498047 C6.94941406,7.94824219 6.54658203,8.60742188 6.64179687,9.75732422 L6.65644531,10.1821289 L8.59736328,10.1821289 Z M7.70380859,13.7124023 C8.52412109,13.7124023 9.02949219,13.2436523 9.02949219,12.4892578 C9.02949219,11.7275391 8.52412109,11.2587891 7.70380859,11.2587891 C6.88349609,11.2587891 6.37080078,11.7275391 6.37080078,12.4892578 C6.37080078,13.2436523 6.88349609,13.7124023 7.70380859,13.7124023 Z" id="path-3"></path>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="2nd-ToolBar-v2-(Perspective)-3" transform="translate(-1161.000000, -7.000000)">
<g id="2nd-ToolBar-buttons-on-right" transform="translate(1076.000000, 5.000000)">
<g id="Helpers-Icon" transform="translate(85.000000, 2.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="#FFFFFF" xlink:href="#path-1"></use>
<g id="?" fill-rule="nonzero" mask="url(#mask-2)">
<use fill="#909090" xlink:href="#path-3"></use>
<use fill="#3F3F3F" xlink:href="#path-3"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Buttons / Dropdown button with Icon / no arrow</title>
<g id="Buttons-/-Dropdown-button-with-Icon-/-no-arrow" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icons-/-System-/-Menu" fill="#FFFFFF">
<rect id="Rectangle-11" x="1.33333333" y="2.66666667" width="13.3333333" height="1.33333333"></rect>
<rect id="Rectangle-11" x="1.33333333" y="7.33333333" width="13.3333333" height="1.33333333"></rect>
<rect id="Rectangle-11" x="1.33333333" y="12" width="13.3333333" height="1.33333333"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 786 B

@@ -28,5 +28,7 @@
<file alias="script_canvas_editor.svg">Menu/script_canvas_editor.svg</file>
<file alias="trackview_editor.svg">Menu/trackview_editor.svg</file>
<file alias="ui_editor.svg">Menu/ui_editor.svg</file>
<file alias="menu.svg">Menu/menu.svg</file>
<file alias="helpers.svg">Menu/helpers.svg</file>
</qresource>
</RCC>
@@ -524,8 +524,6 @@ namespace AzToolsFramework
rootSpawnableIndex = m_playInEditorData.m_assets.size();
}
LoadReferencedAssets(product.GetReferencedAssets());
AZ::Data::AssetInfo info;
info.m_assetId = product.GetAsset().GetId();
info.m_assetType = product.GetAssetType();
@@ -534,6 +532,19 @@ namespace AzToolsFramework
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
// Ensure the product asset is registered with the AssetManager
// Hold on to the returned asset to keep ref count alive until we assign it the latest data
AZ::Data::Asset<AZ::Data::AssetData> asset =
AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
// Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
AZ::Data::AssetManager::Instance().AssignAssetData(m_playInEditorData.m_assets.back());
}
for (auto& product : context.GetProcessedObjects())
{
LoadReferencedAssets(product.GetReferencedAssets());
}
// make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
@@ -326,6 +326,16 @@ namespace AzToolsFramework
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
}
void Instance::DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback)
{
for (auto&& [instanceAlias, instance] : m_nestedInstances)
{
instance->m_parent = nullptr;
callback(AZStd::move(instance));
}
m_nestedInstances.clear();
}
AZStd::unique_ptr<Instance> Instance::DetachNestedInstance(const InstanceAlias& instanceAlias)
{
AZStd::unique_ptr<Instance> removedNestedInstance;
@@ -103,6 +103,7 @@ namespace AzToolsFramework
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
void DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback);
/**
* Gets the aliases for the entities in the Instance DOM.
@@ -151,6 +151,10 @@ namespace AzToolsFramework
return InvalidTemplateId;
}
// Add or replace the Source parameter in the dom
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
sourcePath.Set(readPrefabFileResult.GetValue(), relativePath.Native().c_str());
// Create new Template with the Prefab DOM.
TemplateId newTemplateId = m_prefabSystemComponentInterface->AddTemplate(relativePath, readPrefabFileResult.TakeValue());
if (newTemplateId == InvalidTemplateId)
@@ -922,8 +922,8 @@ namespace AzToolsFramework
return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
}
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
// cannot duplicate an instance from itself.
// If the first entity id is a container entity id, then we need to mark its parent as the common owning instance
// This is because containers, despite representing the nested instance in the parent, are owned by the child.
if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
{
commonOwningInstance = commonOwningInstance->get().GetParentInstance();
@@ -967,17 +967,18 @@ namespace AzToolsFramework
// Duplicate any nested entities and instances as requested
AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
AZStd::unordered_map<EntityAlias, EntityAlias> duplicateEntityAliasMap;
DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
entities, instanceDomAfter, duplicatedEntityAndInstanceIds);
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds,
newInstanceAliasToOldInstanceMap);
entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
command->SetParent(undoBatch.GetUndoBatch());
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
command->Redo();
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
// Create links for our duplicated instances (if any were duplicated)
for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
{
@@ -995,8 +996,35 @@ namespace AzToolsFramework
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
m_prefabSystemComponentInterface->CreateLink(
commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy);
// If the instance was duplicated as part of an ancestor's nested hierarchy, the container's parent patch
// will need to be refreshed to point to the new duplicated parent entity
auto oldInstanceContainerEntityId = oldInstance->GetContainerEntityId();
AZ_Assert(oldInstanceContainerEntityId.IsValid(), "Instance returned invalid Container Entity Id");
AZ::EntityId previousParentEntityId;
AZ::TransformBus::EventResult(previousParentEntityId, oldInstanceContainerEntityId, &AZ::TransformBus::Events::GetParentId);
if (previousParentEntityId.IsValid() && AZStd::find(duplicatedEntityAndInstanceIds.begin(), duplicatedEntityAndInstanceIds.end(), previousParentEntityId))
{
auto oldParentAlias = commonOwningInstance->get().GetEntityAlias(previousParentEntityId);
if (oldParentAlias.has_value() && duplicateEntityAliasMap.contains(oldParentAlias->get()))
{
// Get the dom into a QString for search/replace purposes
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
linkPatchesCopy.Accept(writer);
QString linkPatchesString(buffer.GetString());
ReplaceOldAliases(linkPatchesString, oldParentAlias->get(), duplicateEntityAliasMap[oldParentAlias->get()]);
linkPatchesCopy.Parse(linkPatchesString.toUtf8().constData());
}
}
PrefabUndoHelpers::CreateLink(
oldInstance->GetTemplateId(), commonOwningInstance->get().GetTemplateId(),
AZStd::move(linkPatchesCopy), newInstanceAlias, undoBatch.GetUndoBatch());
}
// Select the duplicated entities/instances
@@ -1211,25 +1239,23 @@ namespace AzToolsFramework
const auto instanceTemplateId = instancePtr->GetTemplateId();
auto parentContainerEntityId = parentInstance.GetContainerEntityId();
instancePtr->GetNestedInstances(
[&](AZStd::unique_ptr<Instance>& nestedInstancePtr)
instancePtr->DetachNestedInstances(
[&](AZStd::unique_ptr<Instance> detachedNestedInstance)
{
//get previous link patch
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstancePtr->GetLinkId());
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
AZ_Assert(
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
nestedInstancePtr->GetLinkId());
PrefabDom& nestedInstanceTemplateDom =
m_prefabSystemComponentInterface->FindTemplateDom(detachedNestedInstance->GetTemplateId());
PrefabDom linkPatchesCopy;
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
RemoveLink(nestedInstancePtr, instanceTemplateId, undoBatch.GetUndoBatch());
UpdateLinkPatchesWithNewEntityAliases(linkPatchesCopy, oldEntityAliases, parentInstance);
Instance& nestedInstanceUnderNewParent = parentInstance.AddInstance(AZStd::move(detachedNestedInstance));
CreateLink(*nestedInstancePtr, parentTemplateId, undoBatch.GetUndoBatch(),
AZStd::move(linkPatchesCopy), true);
PrefabDom nestedInstanceDomUnderNewParent;
m_instanceToTemplateInterface->GenerateDomForInstance(
nestedInstanceDomUnderNewParent, nestedInstanceUnderNewParent);
PrefabDom reparentPatch;
m_instanceToTemplateInterface->GeneratePatch(
reparentPatch, nestedInstanceTemplateDom, nestedInstanceDomUnderNewParent);
CreateLink(nestedInstanceUnderNewParent, parentTemplateId, undoBatch.GetUndoBatch(), AZStd::move(reparentPatch), true);
});
}
@@ -1509,14 +1535,13 @@ namespace AzToolsFramework
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds)
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
{
if (entities.empty())
{
return;
}
AZStd::unordered_map<EntityAlias, EntityAlias> oldAliasToNewAliasMap;
AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
for (AZ::Entity* entity : entities)
@@ -87,7 +87,7 @@ namespace AzToolsFramework
*/
void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
EntityIdList& duplicatedEntityIds);
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap);
/**
* Duplicate a list of instances owned by a common owning instance by directly
* copying/modifying their entries in the instance DOM
@@ -207,6 +207,10 @@ namespace AzToolsFramework
instanceValue->CopyFrom(linkDom, m_prefabDom.GetAllocator());
}
// Remove Source parameter from the dom. It will be added on file load, and should not be stored to disk.
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
sourcePath.Erase(output);
return true;
}
@@ -13,6 +13,7 @@
#include <AzCore/Math/ToString.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/Components/NonUniformScaleComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
@@ -57,6 +58,13 @@ namespace AzToolsFramework
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("EditorNonUniformScaleComponentTypeId", BehaviorConstant(EditorNonUniformScaleComponent::RTTI_Type()))
->Attribute(AZ::Script::Attributes::Module, "editor")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
}
}
void EditorNonUniformScaleComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -214,7 +214,11 @@ namespace AzToolsFramework
class ViewportSettingNotifications
{
public:
virtual void OnGridSnappingChanged(bool enabled) = 0;
virtual void OnGridSnappingChanged([[maybe_unused]] bool enabled) {}
virtual void OnDrawHelpersChanged([[maybe_unused]] bool enabled) {}
protected:
ViewportSettingNotifications() = default;
};
using ViewportSettingsNotificationBus = AZ::EBus<ViewportSettingNotifications, ViewportEBusTraits>;