diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg
new file mode 100644
index 0000000000..f1d36d3e41
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg
@@ -0,0 +1,7 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg
new file mode 100644
index 0000000000..9fc9fe52c2
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg
@@ -0,0 +1,5 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg
new file mode 100644
index 0000000000..7a61db38e0
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg
@@ -0,0 +1,6 @@
+
diff --git a/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg
new file mode 100644
index 0000000000..c6fb977c52
--- /dev/null
+++ b/Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg
@@ -0,0 +1,6 @@
+
diff --git a/Assets/Editor/Translation/scriptcanvas_en_us.ts b/Assets/Editor/Translation/scriptcanvas_en_us.ts
index 937f6a4d96..7b8361c879 100644
--- a/Assets/Editor/Translation/scriptcanvas_en_us.ts
+++ b/Assets/Editor/Translation/scriptcanvas_en_us.ts
@@ -8098,7 +8098,7 @@
COLOR_FROMVALUES_PARAM0_TOOLTIP
- The Red value of hte Color [0, 255]
+ The Red value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM1_NAME
@@ -8107,7 +8107,7 @@
COLOR_FROMVALUES_PARAM1_TOOLTIP
- The Green value of the Color [0, 255]
+ The Green value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM2_NAME
@@ -8116,7 +8116,7 @@
COLOR_FROMVALUES_PARAM2_TOOLTIP
- The Blue value of the Color [0, 255]
+ The Blue value of the Color [0.0-1.0]
COLOR_FROMVALUES_PARAM3_NAME
@@ -8125,7 +8125,7 @@
COLOR_FROMVALUES_PARAM3_TOOLTIP
- The Alpha value of the Color [0, 255]
+ The Alpha value of the Color [0.0-1.0]
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
index 4a26500ee2..52682e70bb 100644
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/CMakeLists.txt
@@ -17,5 +17,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AssetProcessorBatch
AZ::AssetProcessor
)
+
+ ly_add_pytest(
+ NAME AssetPipelineTests.Fbx_Tests
+ PATH ${CMAKE_CURRENT_LIST_DIR}/fbx_test/fbx_test.py
+ TEST_SUITE sandbox
+ RUNTIME_DEPENDENCIES
+ AZ::AssetProcessorBatch
+ AZ::AssetProcessor
+ )
endif()
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
index 2b90f53fc3..5cb61da68e 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/fbx_tests/fbx_tests.py
@@ -34,11 +34,13 @@ logger = logging.getLogger(__name__)
targetProjects = ["AutomatedTesting"]
@pytest.fixture
+@pytest.mark.SUITE_sandbox
def local_resources(request, workspace, ap_setup_fixture):
ap_setup_fixture["tests_dir"] = os.path.dirname(os.path.realpath(__file__))
@dataclass
+@pytest.mark.SUITE_sandbox
class BlackboxAssetTest:
test_name: str
asset_folder: str
@@ -338,9 +340,11 @@ blackbox_fbx_special_tests = [
@pytest.mark.usefixtures("local_resources")
@pytest.mark.parametrize("project", targetProjects)
@pytest.mark.assetpipeline
+@pytest.mark.SUITE_sandbox
class TestsFBX_AllPlatforms(object):
@pytest.mark.BAT
+ @pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_tests)
def test_FBXBlackboxTest_SourceFiles_Processed_ResultInExpectedProducts(self, workspace,
ap_setup_fixture, asset_processor, project,
@@ -359,6 +363,7 @@ class TestsFBX_AllPlatforms(object):
asset_processor, project, blackbox_param)
@pytest.mark.BAT
+ @pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("blackbox_param", blackbox_fbx_special_tests)
def test_FBXBlackboxTest_AssetInfoModified_AssetReprocessed_ResultInExpectedProducts(self,
workspace, ap_setup_fixture,
diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.cpp b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp
new file mode 100644
index 0000000000..94118cdfe4
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/IO/FileReader.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+
+namespace AZ::IO
+{
+ FileReader::FileReader() = default;
+
+ FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
+ {
+ Open(fileIoBase, filePath);
+ }
+
+ FileReader::~FileReader()
+ {
+ Close();
+ }
+
+ FileReader::FileReader(FileReader&& other)
+ {
+ AZStd::swap(m_file, other.m_file);
+ AZStd::swap(m_fileIoBase, other.m_fileIoBase);
+ }
+
+ FileReader& FileReader::operator=(FileReader&& other)
+ {
+ // Close the current file and take over other file
+ Close();
+ m_file = AZStd::move(other.m_file);
+ m_fileIoBase = AZStd::move(other.m_fileIoBase);
+ other.m_file = AZStd::monostate{};
+ other.m_fileIoBase = {};
+
+ return *this;
+ }
+
+ bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
+ {
+ // Close file if the FileReader has an instance open
+ Close();
+
+ if (fileIoBase != nullptr)
+ {
+ AZ::IO::HandleType fileHandle;
+ if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
+ {
+ m_file = fileHandle;
+ m_fileIoBase = fileIoBase;
+ return true;
+ }
+ }
+ else
+ {
+ AZ::IO::SystemFile file;
+ if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
+ {
+ m_file = AZStd::move(file);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool FileReader::IsOpen() const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return *fileHandle != AZ::IO::InvalidHandle;
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->IsOpen();
+ }
+
+ return false;
+ }
+
+ void FileReader::Close()
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr)
+ {
+ fileIo->Close(*fileHandle);
+ }
+ }
+
+ m_file = AZStd::monostate{};
+ m_fileIoBase = {};
+ }
+
+ auto FileReader::Length() const -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
+ {
+ return fileSize;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Length();
+ }
+
+ return 0;
+ }
+
+ auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
+ {
+ return bytesRead;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Read(byteSize, buffer);
+ }
+
+ return 0;
+ }
+
+ auto FileReader::Tell() const -> SizeType
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
+ {
+ return fileOffset;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Tell();
+ }
+
+ return 0;
+ }
+
+ bool FileReader::Seek(AZ::s64 offset, SeekType type)
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return m_fileIoBase->Seek(*fileHandle, offset, type);
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ systemFile->Seek(offset, static_cast(type));
+ return true;
+ }
+
+ return false;
+ }
+
+ bool FileReader::Eof() const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ return m_fileIoBase->Eof(*fileHandle);
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ return systemFile->Eof();
+ }
+
+ return false;
+ }
+
+ bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
+ {
+ if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
+ {
+ AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native();
+ if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity()))
+ {
+ pathStringRef.resize_no_construct(AZStd::char_traits::length(pathStringRef.data()));
+ return true;
+ }
+ }
+ else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
+ {
+ filePath = systemFile->Name();
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Code/Framework/AzCore/AzCore/IO/FileReader.h b/Code/Framework/AzCore/AzCore/IO/FileReader.h
new file mode 100644
index 0000000000..4fdb18b2b2
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/IO/FileReader.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include
+#include
+#include
+
+namespace AZ::IO
+{
+ class FileIOBase;
+ enum class SeekType : AZ::u32;
+
+ //! Structure which encapsulates delegates File Read operations
+ //! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied
+ //! to the FileSystemReader class
+ //! the SettingsRegistry option to use FileIO
+ class FileReader
+ {
+ using HandleType = AZ::u32;
+ using FileHandleType = AZStd::variant;
+ public:
+ using SizeType = AZ::u64;
+
+ //! Creates FileReader instance in the default state with no file opend
+ FileReader();
+ ~FileReader();
+
+ //! Creates a new FileReader instance and attempts to open the file at the supplied path
+ //! Uses the FileIOBase instance if supplied
+ //! @param fileIOBase pointer to fileIOBase instance
+ //! @param null-terminated filePath to open
+ FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
+
+ //! Takes ownership of the supplied FileReader handle
+ FileReader(FileReader&& other);
+
+ //! Moves ownership of FileReader handle to this instance
+ FileReader& operator=(FileReader&& other);
+
+ //! Opens a File using the FileIOBase instance if non-nullptr
+ //! Otherwise fall back to use SystemFile
+ //! @param fileIOBase pointer to fileIOBase instance
+ //! @param null-terminated filePath to open
+ //! @return true if the File is opened successfully
+ bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
+
+ //! Returns true if a file is currently open
+ //! @return true if the file is open
+ bool IsOpen() const;
+
+ //! Closes the File
+ void Close();
+
+ //! Retrieve the length of the OpenFile
+ SizeType Length() const;
+
+ //! Attempts to read up to byte size bytes into the supplied buffer
+ //! @param byteSize - Maximum number of bytes to read
+ //! @param buffer - Buffer to read bytes into
+ //! @returns the number of bytes read if the file is open, otherwise 0
+ SizeType Read(SizeType byteSize, void* buffer);
+
+ //! Returns the current file offset
+ //! @returns file offset if the file is open, otherwise 0
+ SizeType Tell() const;
+
+ //! Seeks within the open file to the offset supplied
+ //! @param offset File offset to seek to
+ //! @param type parameter to indicate the reference point to start the seek from
+ //! @returns true if the file is open and the seek succeeded
+ bool Seek(AZ::s64 offset, SeekType type);
+
+ //! Returns true if the file is open and in the EOF state
+ bool Eof() const;
+
+ //! Store the file path of the open file into the output file path parameter
+ //! The filePath reference is left unmodified, if the path was not stored
+ //! @return true if the filePath was stored
+ bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const;
+
+ private:
+
+ FileHandleType m_file;
+ AZ::IO::FileIOBase* m_fileIoBase{};
+ };
+}
diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
index 5bff79b422..651abb89fe 100644
--- a/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
+++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.cpp
@@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
Platform::Seek(m_handle, this, offset, mode);
}
-SystemFile::SizeType SystemFile::Tell()
+SystemFile::SizeType SystemFile::Tell() const
{
return Platform::Tell(m_handle, this);
}
-bool SystemFile::Eof()
+bool SystemFile::Eof() const
{
return Platform::Eof(m_handle, this);
}
diff --git a/Code/Framework/AzCore/AzCore/IO/SystemFile.h b/Code/Framework/AzCore/AzCore/IO/SystemFile.h
index 8a5b2b2521..551ce89ce7 100644
--- a/Code/Framework/AzCore/AzCore/IO/SystemFile.h
+++ b/Code/Framework/AzCore/AzCore/IO/SystemFile.h
@@ -72,9 +72,9 @@ namespace AZ
/// Seek in current file.
void Seek(SeekSizeType offset, SeekMode mode);
/// Get the cursor position in the current file.
- SizeType Tell();
+ SizeType Tell() const;
/// Is the cursor at the end of the file?
- bool Eof();
+ bool Eof() const;
/// Get the time the file was last modified.
AZ::u64 ModificationTime();
/// Read data from a file synchronous. Return number of bytes actually read in the buffer.
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
index 7ef1fa661d..92f546815e 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1116,118 +1117,6 @@ namespace AZ
}
}
- //! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on
- //! the SettingsRegistry option to use FileIO
- struct SettingsRegistryFileReader
- {
- using FileHandleType = AZStd::variant;
-
- SettingsRegistryFileReader() = default;
- SettingsRegistryFileReader(bool useFileIo, const char* filePath)
- {
- Open(useFileIo, filePath);
- }
-
- ~SettingsRegistryFileReader()
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
- {
- fileIo->Close(*fileHandle);
- }
- }
- }
-
- bool Open(bool useFileIo, const char* filePath)
- {
- Close();
- if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr)
- {
- AZ::IO::HandleType fileHandle;
- if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
- {
- m_file = fileHandle;
- return true;
- }
- }
- else
- {
- AZ::IO::SystemFile file;
- if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
- {
- m_file = AZStd::move(file);
- return true;
- }
- }
-
- return false;
- }
-
- bool IsOpen() const
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- return *fileHandle != AZ::IO::InvalidHandle;
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->IsOpen();
- }
-
- return false;
- }
-
- void Close()
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
- {
- fileIo->Close(*fileHandle);
- }
- }
-
- m_file = AZStd::monostate{};
- }
-
- u64 Length() const
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
- {
- return fileSize;
- }
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->Length();
- }
-
- return 0;
- }
-
- AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer)
- {
- if (auto fileHandle = AZStd::get_if(&m_file); fileHandle != nullptr)
- {
- if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
- {
- return bytesRead;
- }
- }
- else if (auto systemFile = AZStd::get_if(&m_file); systemFile != nullptr)
- {
- return systemFile->Read(byteSize, buffer);
- }
-
- return 0;
- }
-
- FileHandleType m_file;
- };
-
bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey,
AZStd::vector& scratchBuffer)
{
@@ -1236,7 +1125,7 @@ namespace AZ
Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
- SettingsRegistryFileReader fileReader(m_useFileIo, path);
+ FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path);
if (!fileReader.IsOpen())
{
AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path);
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 5290c9b02a..da7110e36e 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -6,6 +6,8 @@
*
*/
+#include
+#include
#include
#include
#include
@@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils
const ConfigParserSettings& configParserSettings)
{
auto configPath = FindEngineRoot(registry) / filePath;
- IO::SystemFile configFile;
- if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
+ IO::FileReader configFile;
+ bool configFileOpened{};
+ switch (configParserSettings.m_fileReaderClass)
+ {
+ case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile:
+ {
+ auto fileIo = AZ::IO::FileIOBase::GetInstance();
+ configFileOpened = configFile.Open(fileIo, configPath.c_str());
+ break;
+ }
+ case ConfigParserSettings::FileReaderClass::UseSystemFileOnly:
+ {
+ configFileOpened = configFile.Open(nullptr, configPath.c_str());
+ break;
+ }
+ case ConfigParserSettings::FileReaderClass::UseFileIOOnly:
+ {
+ auto fileIo = AZ::IO::FileIOBase::GetInstance();
+ if (fileIo == nullptr)
+ {
+ return false;
+ }
+ configFileOpened = configFile.Open(fileIo, configPath.c_str());
+ break;
+ }
+ default:
+ AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied");
+ return false;
+ }
+ if (!configFileOpened)
{
AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str());
return false;
@@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ_Error("SettingsRegistryMergeUtils", false,
R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n"
R"(Parsing will halt. The line content so far is:)" "\n"
- R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(),
+ R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(),
aznumeric_cast(configBuffer.size()), configBuffer.data());
configFileParsed = false;
break;
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
index 02346c2ba1..daa64c0343 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
@@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils
//! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function
//! The structure contains a functor which returns true if a character is a valid delimiter
SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings;
+
+ //! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile
+ enum class FileReaderClass
+ {
+ UseFileIOIfAvailableFallbackToSystemFile,
+ UseSystemFileOnly,
+ UseFileIOOnly
+ };
+ FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile;
};
//! Loads basic configuration files which have structures similar to Windows INI files
//! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 6c498c3335..aa07959997 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -166,6 +166,8 @@ set(FILES
IO/FileIO.cpp
IO/FileIO.h
IO/FileIOEventBus.h
+ IO/FileReader.cpp
+ IO/FileReader.h
IO/IOUtils.h
IO/IOUtils.cpp
IO/IStreamer.h
diff --git a/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp
new file mode 100644
index 0000000000..691b3f2821
--- /dev/null
+++ b/Code/Framework/AzCore/Tests/IO/FileReaderTests.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#include
+#include
+#include
+
+namespace UnitTest
+{
+ template
+ class FileReaderTestFixture
+ : public ScopedAllocatorSetupFixture
+ {
+ public:
+ void SetUp() override
+ {
+ if constexpr (AZStd::is_same_v)
+ {
+ m_fileIo = AZStd::make_unique();
+ }
+ }
+
+ void TearDown() override
+ {
+ m_fileIo.reset();
+ }
+
+ protected:
+ AZStd::unique_ptr m_fileIo{};
+ };
+
+ using FileIOTypes = ::testing::Types;
+
+ TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes);
+
+ TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.IsOpen());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully)
+ {
+ AZ::IO::FileReader fileReader;
+ fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.IsOpen());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ EXPECT_TRUE(fileReader.Eof());
+ }
+
+ TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds)
+ {
+ AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
+ AZ::IO::FixedMaxPath filePath;
+ EXPECT_TRUE(fileReader.GetFilePath(filePath));
+ AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() };
+ if (this->m_fileIo)
+ {
+ EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename));
+ }
+ EXPECT_EQ(nulFilename, filePath);
+ }
+
+} // namespace UnitTest
diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
index d4d107f094..c36d37d874 100644
--- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake
+++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake
@@ -37,6 +37,7 @@ set(FILES
FileIOBaseTestTypes.h
Geometry2DUtils.cpp
Interface.cpp
+ IO/FileReaderTests.cpp
IO/Path/PathTests.cpp
IPC.cpp
Jobs.cpp
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
index 469a235b9f..ec709aef8a 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp
@@ -138,7 +138,13 @@ namespace AzToolsFramework
m_indexMap[row] = index;
m_rowMap[index] = row;
++row;
- ++m_displayedItemsCounter;
+
+ // We only want to increase the displayed counter if it is a parent (Source)
+ // so we don't cut children entries.
+ if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
+ {
+ ++m_displayedItemsCounter;
+ }
}
if (model->hasChildren(index))
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
index 37091c11e2..217e282a37 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.cpp
@@ -29,20 +29,20 @@ namespace AzToolsFramework
{
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: AzQtComponents::TableView(parent)
- , m_delegate(new EntryDelegate(this))
+ , m_delegate(new SearchEntryDelegate(this))
{
- setSortingEnabled(true);
+ setSortingEnabled(false);
setItemDelegate(m_delegate);
setRootIsDecorated(false);
//Styling the header aligning text to the left and using a bold font.
header()->setDefaultAlignment(Qt::AlignLeft);
- header()->setStyleSheet("QHeaderView { font-weight: bold; }");
+ header()->setStyleSheet("QHeaderView { font-weight: bold; };");
+
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
- setSortingEnabled(false);
setSelectionMode(QAbstractItemView::SingleSelection);
connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
@@ -67,6 +67,8 @@ namespace AzToolsFramework
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
+ header()->setSortIndicatorShown(false);
+ header()->setSectionsClickable(false);
}
void AssetBrowserTableView::SetName(const QString& name)
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
index 94e9bdc4e4..b4eca59cef 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
@@ -26,7 +26,7 @@ namespace AzToolsFramework
class AssetBrowserEntry;
class AssetBrowserTableModel;
class AssetBrowserFilterModel;
- class EntryDelegate;
+ class SearchEntryDelegate;
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
: public AzQtComponents::TableView
@@ -67,9 +67,9 @@ namespace AzToolsFramework
private:
QString m_name;
- QPointer m_tableModel = nullptr;
- QPointer m_sourceFilterModel = nullptr;
- EntryDelegate* m_delegate = nullptr;
+ QPointer m_tableModel;
+ QPointer m_sourceFilterModel;
+ SearchEntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
index 29324c612c..755c59b55b 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp
@@ -11,12 +11,13 @@
#include
#include
#include
-
+#include
#include
#include
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
+#include
#include
AZ_POP_DISABLE_WARNING
@@ -24,8 +25,13 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
- const int ENTRY_SPACING_LEFT_PIXELS = 8;
- const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2;
+ static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg";
+ static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg";
+ static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg";
+ static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg";
+
+ const int EntrySpacingLeftPixels = 8;
+ const int EntryIconMarginLeftPixels = 2;
EntryDelegate::EntryDelegate(QWidget* parent)
: QStyledItemDelegate(parent)
@@ -62,7 +68,7 @@ namespace AzToolsFramework
// Draw main entry thumbnail.
QRect remainingRect(option.rect);
- remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
QSize iconSize(m_iconSize, m_iconSize);
// Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
@@ -89,7 +95,7 @@ namespace AzToolsFramework
}
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
- remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
+ remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
}
QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)
? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name)))
@@ -148,7 +154,162 @@ namespace AzToolsFramework
return m_iconSize;
}
- } // namespace Thumbnailer
+ SearchEntryDelegate::SearchEntryDelegate(QWidget* parent)
+ : EntryDelegate(parent)
+ {
+ LoadBranchPixMaps();
+ }
+
+ void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+ {
+ auto data = index.data(AssetBrowserModel::Roles::EntryRole);
+ if (data.canConvert())
+ {
+ bool isEnabled = (option.state & QStyle::State_Enabled) != 0;
+ bool isSelected = (option.state & QStyle::State_Selected) != 0;
+
+ QStyle* style = option.widget ? option.widget->style() : QApplication::style();
+
+ // draw the background
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget);
+
+ // Draw main entry thumbnail.
+ QRect remainingRect(option.rect);
+
+ QSize iconSize(m_iconSize, m_iconSize);
+ // Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
+ // so it needs to center vertically with padding in that case:
+ QPoint iconTopLeft;
+ QPoint branchIconTopLeft = QPoint();
+
+ auto entry = qvariant_cast(data);
+ auto sourceEntry = azrtti_cast(entry);
+
+ //If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon
+ if (sourceEntry || index.column() != aznumeric_cast(AssetBrowserEntry::Column::Name))
+ {
+ remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ }
+ else
+ {
+ remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon.
+ iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
+ }
+
+ QPalette actualPalette(option.palette);
+
+ if (index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name))
+ {
+ int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
+ if (sourceEntry)
+ {
+ if (m_showSourceControl)
+ {
+ DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
+ }
+ // sources with no children should be greyed out.
+ if (sourceEntry->GetChildCount() == 0)
+ {
+ isEnabled = false; // draw in disabled style.
+ actualPalette.setCurrentColorGroup(QPalette::Disabled);
+ }
+ }
+ else
+ {
+ //Get the indexes above and below our entry to see what type are they.
+ QAbstractItemView* view = qobject_cast(option.styleObject);
+ const QAbstractItemModel* viewModel = view->model();
+
+ const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column());
+ const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column());
+
+ auto aboveEntry = qvariant_cast(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
+ auto belowEntry = qvariant_cast(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
+
+ auto aboveSourceEntry = azrtti_cast(aboveEntry);
+ auto belowSourceEntry = azrtti_cast(belowEntry);
+
+ // if current index is the last entry in the view
+ // or the index above it is a Source Entry and
+ // the index below is invalid or is valid but it is also a source entry
+ // then the current index is the only child.
+ if (index.row() == viewModel->rowCount() - 1 ||
+ (indexBelow.isValid() && aboveSourceEntry &&
+ (!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry))))
+ {
+ DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon
+ }
+ else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry
+ {
+ DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon
+ }
+ else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry
+ {
+ DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon
+ }
+ else //the index above and below are also child entries
+ {
+ DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon.
+ }
+ }
+
+ remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
+ remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
+ }
+ QString displayString = index.column() == aznumeric_cast(AssetBrowserEntry::Column::Name)
+ ? qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Name)))
+ : qvariant_cast(entry->data(aznumeric_cast(AssetBrowserEntry::Column::Path)));
+
+ style->drawItemText(
+ painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString,
+ isSelected ? QPalette::HighlightedText : QPalette::Text);
+ }
+ }
+
+ void SearchEntryDelegate::LoadBranchPixMaps()
+ {
+ AZ::IO::BasicPath absoluteIconPath;
+ for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType)
+ {
+ QPixmap pixmap;
+ switch (branchType)
+ {
+ case AzToolsFramework::AssetBrowser::EntryBranchType::First:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::Middle:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::Last:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast;
+ break;
+ case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild:
+ default:
+ absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild;
+ break;
+ }
+ bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str());
+ AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate");
+
+ m_branchIcons[static_cast(branchType)] = pixmap;
+
+ }
+ }
+
+ void SearchEntryDelegate::DrawBranchPixMap(
+ EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const
+ {
+ const QPixmap& pixmap = m_branchIcons[branchType];
+
+ pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ const QSize sizeDelta = size - pixmap.size();
+ const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2);
+ painter->drawPixmap(point + pointDelta, pixmap);
+ }
+
+ } // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Views/moc_EntryDelegate.cpp"
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
index 643bedec7c..ac68c19248 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h
@@ -27,9 +27,19 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
+ //! Type of branch icon the delegate should paint.
+ enum EntryBranchType
+ {
+ First,
+ Middle,
+ Last,
+ OneChild,
+ Count
+ };
+
class AssetBrowserFilterModel;
- //! EntryDelegate draws a single item in AssetBrowser
+ //! EntryDelegate draws a single item in AssetBrowser.
class EntryDelegate
: public QStyledItemDelegate
{
@@ -52,5 +62,23 @@ namespace AzToolsFramework
//! Draw a thumbnail and return its width
int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const;
};
+
+ //! SearchEntryDelegate draws a single item in AssetBrowserTableView.
+ class SearchEntryDelegate
+ : public EntryDelegate
+ {
+ Q_OBJECT
+ public:
+ explicit SearchEntryDelegate(QWidget* parent = nullptr);
+
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+
+ private:
+ void LoadBranchPixMaps();
+ void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const;
+
+ private:
+ QMap m_branchIcons;
+ };
} // namespace AssetBrowser
} // namespace AzToolsFramework
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h
index 478c925299..9af22b1fdf 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkCharacterComponent.h
@@ -19,6 +19,22 @@ namespace Physics
namespace Multiplayer
{
+ //! NetworkCharacterRequests
+ //! ComponentBus handled by NetworkCharacterComponentController.
+ //! Bus was created for exposing controller methods to script; C++ users should access the controller directly.
+ class NetworkCharacterRequests : public AZ::ComponentBus
+ {
+ public:
+ //! TryMoveWithVelocity
+ //! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
+ //! Velocity will be applied over delta-time to determine the movement amount.
+ //! Returns this entity's world-space position after the move.
+ virtual AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) = 0;
+ };
+
+ typedef AZ::EBus NetworkCharacterRequestBus;
+
+
//! NetworkCharacterComponent
//! Provides multiplayer support for game-play player characters.
class NetworkCharacterComponent
@@ -39,6 +55,12 @@ namespace Multiplayer
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
}
+ static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+ {
+ NetworkCharacterComponentBase::GetRequiredServices(required);
+ required.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
+ }
+
// AZ::Component
void OnInit() override {}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
@@ -65,18 +87,22 @@ namespace Multiplayer
//! Class provides the ability to move characters in physical space while keeping the network in-sync.
class NetworkCharacterComponentController
: public NetworkCharacterComponentControllerBase
+ , private NetworkCharacterRequestBus::Handler
{
public:
+ AZ_RTTI(NetworkCharacterComponentController, "{C91851A2-8B95-4484-9F97-BFF9D1F528A0}")
+ static void Reflect(AZ::ReflectContext* context);
NetworkCharacterComponentController(NetworkCharacterComponent& parent);
// NetworkCharacterComponentControllerBase
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
+ // NetworkCharacterRequestBus::Handler
//! TryMoveWithVelocity
//! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
//! Velocity will be applied over delta-time to determine the movement amount.
//! Returns this entity's world-space position after the move.
- AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime);
+ AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime) override;
};
}
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h
index 19379fc959..cec73b1b71 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkRigidBodyComponent.h
@@ -1,5 +1,6 @@
/*
- * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja
index eb4b81ea96..cf62f6f901 100644
--- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja
+++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja
@@ -909,6 +909,7 @@ enum class NetworkProperties
controller->Set{{ UpperFirst(Property.attrib['Name']) }}({{ LowerFirst(Property.attrib['Name']) }});
{% endif %}
})
+{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' -%}
->Method("GetOn{{ UpperFirst(Property.attrib['Name']) }}ChangedEvent", [](AZ::EntityId id) -> AZ::Event*
{% else %}
@@ -936,6 +937,7 @@ enum class NetworkProperties
{% else %}
->Attribute(AZ::Script::Attributes::AzEventDescription, AZ::BehaviorAzEventDescription{ "On {{ UpperFirst(Property.attrib['Name']) }} Changed Event", {"New {{ Property.attrib['Type'] }}"} })
{% endif %}
+{% endif %}
{% endif %}
{% endcall %}
@@ -1518,7 +1520,7 @@ namespace {{ Component.attrib['Namespace'] }}
{{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}}
{{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) }}
- behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}")
+ behaviorContext->Class<{{ ComponentBaseName }}>("{{ ComponentBaseName }}")
->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}")
->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}")
diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp
index b14fc8761f..33eb26653a 100644
--- a/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp
@@ -83,7 +83,7 @@ namespace Multiplayer
return physx::PxQueryHitType::eNONE;
}
- void NetworkCharacterComponent::NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
+ void NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
@@ -92,6 +92,7 @@ namespace Multiplayer
->Version(1);
}
NetworkCharacterComponentBase::Reflect(context);
+ NetworkCharacterComponentController::Reflect(context);
}
NetworkCharacterComponent::NetworkCharacterComponent()
@@ -161,6 +162,18 @@ namespace Multiplayer
return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0;
}
+ void NetworkCharacterComponentController::Reflect(AZ::ReflectContext* context)
+ {
+ if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context))
+ {
+ behaviorContext->EBus("NetworkCharacterRequestBus")
+ ->Event("TryMoveWithVelocity", &NetworkCharacterRequestBus::Events::TryMoveWithVelocity, {{ { "Velocity" }, { "DeltaTime" } }});
+
+ behaviorContext->Class("NetworkCharacterComponentController")
+ ->RequestBus("NetworkCharacterRequestBus");
+ }
+ }
+
NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent)
: NetworkCharacterComponentControllerBase(parent)
{
@@ -169,12 +182,12 @@ namespace Multiplayer
void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
- ;
+ NetworkCharacterRequestBus::Handler::BusConnect(GetEntity()->GetId());
}
void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
- ;
+ NetworkCharacterRequestBus::Handler::BusDisconnect(GetEntity()->GetId());
}
AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime)
diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp
index bcf855d834..725ebc024c 100644
--- a/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/Components/NetworkRigidBodyComponent.cpp
@@ -1,5 +1,6 @@
/*
- * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*