Merge pull request #1489 from aws-lumberyard-dev/mbalfour/gitflow_210622
Integration from stabilization/2106 at commit d5490ecf69 .
This commit is contained in:
@@ -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>
|
||||
|
||||
+13
-2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user