Merge main to mp_editor_pipeline

This commit is contained in:
puvvadar
2021-05-16 11:36:47 -07:00
91 changed files with 1085 additions and 588 deletions
@@ -477,7 +477,7 @@ namespace AZ
m_console = AZ::Interface<AZ::IConsole>::Get();
if (m_console == nullptr)
{
m_console = aznew AZ::Console();
m_console = aznew AZ::Console(*m_settingsRegistry);
AZ::Interface<AZ::IConsole>::Register(m_console);
m_ownsConsole = true;
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
+176 -38
View File
@@ -13,7 +13,9 @@
#include <AzCore/Console/Console.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
#include <AzCore/Settings/CommandLine.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/IO/FileIO.h>
@@ -43,6 +45,12 @@ namespace AZ
{
}
Console::Console(AZ::SettingsRegistryInterface& settingsRegistryInterface)
: Console()
{
RegisterCommandInvokerWithSettingsRegistry(settingsRegistryInterface);
}
Console::~Console()
{
// on console destruction relink the console functors back to the deferred head
@@ -111,51 +119,51 @@ namespace AZ
void Console::ExecuteConfigFile(AZStd::string_view configFileName)
{
IO::FixedMaxPath filePathFixed = configFileName;
if (AZ::IO::FileIOBase* fileIOBase = AZ::IO::FileIOBase::GetInstance())
auto settingsRegistry = AZ::SettingsRegistry::Get();
// If the config file is a settings registry file use the SettingsRegistryInterface MergeSettingsFile function
// otherwise use the SettingsRegistryMergeUtils MergeSettingsToRegistry_ConfigFile function to merge an INI-style
// file to the settings registry
AZ::IO::PathView configFile(configFileName);
if (configFile.Extension() == ".setreg")
{
fileIOBase->ResolvePath(filePathFixed, configFileName);
settingsRegistry->MergeSettingsFile(configFile.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch);
}
IO::SystemFile file;
if (!file.Open(filePathFixed.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
else if (configFile.Extension() == ".setregpatch")
{
AZLOG_ERROR("Failed to load '%s'. File could not be opened.", filePathFixed.c_str());
return;
settingsRegistry->MergeSettingsFile(configFile.Native(), AZ::SettingsRegistryInterface::Format::JsonPatch);
}
const IO::SizeType length = file.Length();
if (length == 0)
else
{
AZLOG_ERROR("Failed to load '%s'. File is empty.", filePathFixed.c_str());
return;
}
file.Seek(0, IO::SystemFile::SF_SEEK_BEGIN);
AZStd::string fileBuffer;
fileBuffer.resize(length);
IO::SizeType bytesRead = file.Read(length, fileBuffer.data());
file.Close();
// Resize again just in case bytesRead is less than length for some reason
fileBuffer.resize(bytesRead);
AZLOG_INFO("Loading config file %s", filePathFixed.c_str());
AZStd::vector<AZStd::string_view> separatedCommands;
auto BreakCommandsByLine = [&separatedCommands](AZStd::string_view token)
{
separatedCommands.emplace_back(token);
};
StringFunc::TokenizeVisitor(fileBuffer, BreakCommandsByLine, "\n\r");
for (const auto& commandView : separatedCommands)
{
ConsoleCommandContainer commandArgsView;
auto ConvertCommandStringToArray = [&commandArgsView](AZStd::string_view token)
AZ::SettingsRegistryMergeUtils::ConfigParserSettings configParserSettings;
configParserSettings.m_registryRootPointerPath = "/Amazon/AzCore/Runtime/ConsoleCommands";
configParserSettings.m_commandLineSettings.m_delimiterFunc = [](AZStd::string_view line)
{
commandArgsView.emplace_back(token);
SettingsRegistryInterface::CommandLineArgumentSettings::JsonPathValue pathValue;
AZStd::string_view parsedLine = line;
// Splits the line based on the <equal> or <colon>
if (auto path = AZ::StringFunc::TokenizeNext(parsedLine, "=:"); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
pathValue.m_value = AZ::StringFunc::StripEnds(parsedLine);
}
// If the value is empty, then the line either contained an equal sign followed only by whitespace or the line was empty
// 1. line="testInit=", pathValue.m_path="testInit", pathValue.m_value=""
// 2. line="testInit 1", pathValue.m_path="testInit 1", pathValue.m_value=""
// Therefore the path is split the path on whitespace in order to retrieve a value
if (pathValue.m_value.empty())
{
parsedLine = pathValue.m_path;
if (auto path = AZ::StringFunc::TokenizeNext(parsedLine, " \t"); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
pathValue.m_value = AZ::StringFunc::StripEnds(parsedLine);
}
}
return pathValue;
};
constexpr AZStd::string_view commandSeparators = " =";
StringFunc::TokenizeVisitor(commandView, ConvertCommandStringToArray, commandSeparators);
PerformCommand(commandArgsView, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ConfigFile(*settingsRegistry, configFile.Native(), configParserSettings);
}
}
@@ -447,4 +455,134 @@ namespace AZ
return result;
}
struct ConsoleCommandKeyNotificationHandler
{
ConsoleCommandKeyNotificationHandler(AZ::SettingsRegistryInterface& registry, Console& console)
: m_settingsRegistry(registry)
, m_console(console)
{
}
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
&& inputKey.IsRelativeTo(consoleRootCommandKey))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
operator()(path, type);
}
}
// This is the default issue reporting, that logs using the warning category
if (result.GetProcessing() != JsonSerializationResult::Processing::Completed)
{
scratchBuffer.append(message.begin(), message.end());
scratchBuffer.append("\n Reason: ");
result.AppendToString(scratchBuffer, path);
scratchBuffer.append(".");
AZ_Warning("JSON Serialization", false, "%s", scratchBuffer.c_str());
scratchBuffer.clear();
}
return result;
}
void operator()(AZStd::string_view path, SettingsRegistryInterface::Type type)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (inputKey.IsRelativeTo(consoleRootCommandKey))
{
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
// and therefore doesn't own the memory.
FixedValueString commandArgString;
if (type == SettingsRegistryInterface::Type::String)
{
if (m_settingsRegistry.Get(commandArgString, path))
{
auto ConvertCommandArgumentToArray = [&commandArgs](AZStd::string_view token)
{
commandArgs.emplace_back(token);
};
constexpr AZStd::string_view commandSeparators = " \t\n\r";
StringFunc::TokenizeVisitor(commandArgString, ConvertCommandArgumentToArray, commandSeparators);
}
}
else if (type == SettingsRegistryInterface::Type::Boolean)
{
bool commandArgBool{};
if (m_settingsRegistry.Get(commandArgBool, path))
{
commandArgString = commandArgBool ? "true" : "false";
commandArgs.emplace_back(commandArgString);
}
}
else if (type == SettingsRegistryInterface::Type::Integer)
{
// Try converting to a signed 64-bit number first and then an unsigned 64-bit number
AZ::s64 commandArgInt{};
AZ::u64 commandArgUInt{};
if (m_settingsRegistry.Get(commandArgInt, path))
{
AZStd::to_string(commandArgString, commandArgInt);
commandArgs.emplace_back(commandArgString);
}
else if (m_settingsRegistry.Get(commandArgUInt, path))
{
AZStd::to_string(commandArgString, commandArgUInt);
commandArgs.emplace_back(commandArgString);
}
}
else if (type == SettingsRegistryInterface::Type::FloatingPoint)
{
double commandArgFloat{};
if (m_settingsRegistry.Get(commandArgFloat, path))
{
AZStd::to_string(commandArgString, commandArgFloat);
commandArgs.emplace_back(commandArgString);
}
}
CVarFixedString commandTrace(command);
for (AZStd::string_view commandArg : commandArgs)
{
commandTrace.push_back(' ');
commandTrace += commandArg;
}
m_console.PerformCommand(command, commandArgs, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
}
}
AZ::Console& m_console;
AZ::SettingsRegistryInterface& m_settingsRegistry;
AZStd::string scratchBuffer;
};
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
JsonApplyPatchSettings applyPatchSettings;
applyPatchSettings.m_reporting = ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this };
settingsRegistry.SetApplyPatchSettings(applyPatchSettings);
}
}
@@ -14,6 +14,7 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -29,6 +30,9 @@ namespace AZ
AZ_CLASS_ALLOCATOR(Console, AZ::OSAllocator, 0);
Console();
//! Constructor overload which registers a notifier with the Settings Registry that will execute
//! a console command whenever a key is set under the AZ::IConsole::ConsoleCommandRootKey JSON object
explicit Console(AZ::SettingsRegistryInterface& settingsRegistry);
~Console() override;
//! IConsole interface
@@ -67,6 +71,7 @@ namespace AZ
void RegisterFunctor(ConsoleFunctorBase* functor) override;
void UnregisterFunctor(ConsoleFunctorBase* functor) override;
void LinkDeferredFunctors(ConsoleFunctorBase*& deferredHead) override;
void RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry) override;
//! @}
private:
@@ -96,6 +101,7 @@ namespace AZ
ConsoleFunctorBase* m_head;
using CommandMap = AZStd::unordered_map<CVarFixedString, AZStd::vector<ConsoleFunctorBase*>>;
CommandMap m_commands;
AZ::SettingsRegistryInterface::NotifyEventHandler m_consoleCommandKeyHandler;
friend class ConsoleFunctorBase;
};
@@ -148,7 +148,15 @@ namespace AZ
{
AZ::CVarFixedString convertCandidate{ arguments.front() };
char* endPtr = nullptr;
MAX_TYPE value = static_cast<MAX_TYPE>(strtoll(convertCandidate.c_str(), &endPtr, 0));
MAX_TYPE value;
if constexpr (AZStd::is_unsigned_v<MAX_TYPE>)
{
value = aznumeric_cast<MAX_TYPE>(strtoull(convertCandidate.c_str(), &endPtr, 0));
}
else
{
value = aznumeric_cast<MAX_TYPE>(strtoll(convertCandidate.c_str(), &endPtr, 0));
}
if (endPtr == convertCandidate.c_str())
{
@@ -22,8 +22,10 @@
namespace AZ
{
class SettingsRegistryInterface;
class CommandLine;
//! @class IConsole
//! A simple console class for providing text based variable and process interaction.
class IConsole
@@ -33,6 +35,8 @@ namespace AZ
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
@@ -145,6 +149,12 @@ namespace AZ
//! Returns the AZ::Event<> invoked whenever a console command could not be found.
DispatchCommandNotFoundEvent& GetDispatchCommandNotFoundEvent();
//! Register a notification event handler with the Settings Registry
//! That is responsible for updating console commands whenever
//! a key is found underneath the "/Amazon/AzCore/Runtime/ConsoleCommands" JSON entry
//! @param Settings Registry reference to register notifier with
virtual void RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry) = 0;
AZ_DISABLE_COPY_MOVE(IConsole);
protected:
@@ -11,13 +11,17 @@
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/Serialization/Json/JsonMerger.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/osstring.h>
namespace AZ
{
using ReporterString = AZStd::fixed_string<1024>;
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings)
@@ -105,8 +109,7 @@ namespace AZ
}
else
{
AZ::OSString message = AZ::OSString::format(R"(Unknown operation "%.*s".)",
aznumeric_cast<int>(operationName.length()), operationName.data());
auto message = ReporterString::format(R"(Unknown operation "%.*s".)", AZ_STRING_ARG(operationName));
return settings.m_reporting(message.c_str(), ResultCode(Tasks::Merge, Outcomes::Unknown), element);
}
@@ -131,6 +134,14 @@ namespace AZ
JsonSerializationResult::ResultCode JsonMerger::ApplyMergePatch(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings)
{
StackedString element(StackedString::Format::JsonPointer);
return ApplyMergePatchInternal(target, allocator, patch, settings, element);
}
JsonSerializationResult::ResultCode JsonMerger::ApplyMergePatchInternal(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings, StackedString& element)
{
using namespace JsonSerializationResult;
@@ -150,14 +161,18 @@ namespace AZ
{
if (targetField != target.MemberEnd())
{
result.Combine(ApplyMergePatch(targetField->value, allocator, field.value, settings));
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
result.Combine(ApplyMergePatchInternal(targetField->value, allocator, field.value, settings, element));
}
else
{
rapidjson::Value name;
name.CopyFrom(field.name, allocator, true);
rapidjson::Value value;
result.Combine(ApplyMergePatch(value, allocator, field.value, settings));
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
result.Combine(ApplyMergePatchInternal(value, allocator, field.value, settings, element));
target.AddMember(AZStd::move(name), AZStd::move(value), allocator);
}
}
@@ -165,7 +180,14 @@ namespace AZ
{
if (targetField != target.MemberEnd())
{
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
target.RemoveMember(targetField);
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully removed member from "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
}
else
@@ -173,6 +195,12 @@ namespace AZ
if (targetField != target.MemberEnd())
{
targetField->value.CopyFrom(field.value, allocator, true);
ScopedStackedString fieldNameScope{ element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully updated JSON field "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
else
{
@@ -181,6 +209,12 @@ namespace AZ
name.CopyFrom(field.name, allocator, true);
value.CopyFrom(field.value, allocator, true);
target.AddMember(AZStd::move(name), AZStd::move(value), allocator);
ScopedStackedString fieldNameScope{ element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully added JSON field "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
}
}
@@ -190,7 +224,7 @@ namespace AZ
target.CopyFrom(patch, allocator, true);
}
result.Combine(settings.m_reporting("Successfully applied patch to target using JSON Merge Patch.",
ResultCode(Tasks::Merge, Outcomes::Success), StackedString(StackedString::Format::JsonPointer)));
ResultCode(Tasks::Merge, Outcomes::Success), element));
return result;
}
@@ -268,9 +302,11 @@ namespace AZ
const rapidjson::Pointer::Token* const tokens = path.GetTokens();
if (path.GetTokenCount() == 0)
{
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
target = AZStd::move(newValue);
return settings.m_reporting(R"(Successfully applied "add" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
rapidjson::Pointer parent = rapidjson::Pointer(tokens, path.GetTokenCount() - 1);
@@ -342,8 +378,10 @@ namespace AZ
ResultCode(Tasks::Merge, Outcomes::TypeMismatch), element);
}
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "add" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Remove(rapidjson::Value& target, const rapidjson::Pointer& path,
@@ -393,8 +431,10 @@ namespace AZ
ResultCode(Tasks::Merge, Outcomes::TypeMismatch), element);
}
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "remove" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Replace(rapidjson::Value& target,
@@ -420,8 +460,10 @@ namespace AZ
memberValue->CopyFrom(value->value, allocator);
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "replace" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Move(rapidjson::Value& target,
@@ -42,6 +42,11 @@ namespace AZ
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings);
//! Implementation of the JSON Merge Patch algorithm: https://tools.ietf.org/html/rfc7386
static JsonSerializationResult::ResultCode ApplyMergePatchInternal(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings, StackedString& element);
//! Function to create JSON Merge Patches: https://tools.ietf.org/html/rfc7386
static JsonSerializationResult::ResultCode CreateMergePatch(rapidjson::Value& patch,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& source,
@@ -88,4 +88,28 @@ namespace AZ
{
return index < m_names.size() ? m_names[index] : AZStd::string_view();
}
SettingsRegistryInterface::CommandLineArgumentSettings::CommandLineArgumentSettings()
{
m_delimiterFunc = [](AZStd::string_view line) -> JsonPathValue
{
constexpr AZStd::string_view CommandLineArgumentDelimiters{ "=:" };
JsonPathValue pathValue;
pathValue.m_value = line;
// Splits the line on the first delimiter and stores that in the pathValue.m_path variable
// The StringFunc::TokenizeNext function updates the pathValue.m_value parameter in place
// to contain all the text after the first delimiter
// So if pathValue.m_value="foo = Hello Ice Cream=World:17", the call to TokenizeNext would
// split the value as follows
// pathValue.m_path = "foo"
// pathValue.m_value = "Hello Ice Cream=World:17"
if (auto path = AZ::StringFunc::TokenizeNext(pathValue.m_value, CommandLineArgumentDelimiters); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
}
pathValue.m_value = AZ::StringFunc::StripEnds(pathValue.m_value);
return pathValue;
};
}
} // namespace AZ
@@ -26,6 +26,7 @@
namespace AZ
{
struct JsonApplyPatchSettings;
//! The Settings Registry is the central storage for global settings. Having application-wide settings
//! stored in a central location allows different tools such as command lines, consoles, configuration
//! files, etc. to work in a universal way.
@@ -260,21 +261,20 @@ namespace AZ
virtual bool Remove(AZStd::string_view path) = 0;
//! Structure which contains configuration settings for how to parse a single command line argument
//! It supports supplying a functor for determining if a character is a delimiter
//! It supports supplying a functor for splitting a line into JSON path and JSON value
struct CommandLineArgumentSettings
{
inline static constexpr AZStd::string_view CommandLineArgumentDelimiters{ "=:"};
CommandLineArgumentSettings()
struct JsonPathValue
{
m_delimiterFunc = [](const char delimiter) -> bool
{
return CommandLineArgumentDelimiters.find_first_of(delimiter) != AZStd::string_view::npos;
};
}
//! Callback function which is invoked to determine whether a delimiter has been found
//! return value of true indicates that a delimiter has been found
using DelimiterFunc = AZStd::function<bool(const char delimiter)>;
AZStd::string_view m_path;
AZStd::string_view m_value;
};
CommandLineArgumentSettings();
//! Callback function which is invoked to determine how to split a command line argument
//! into a JSON path and a JSON value
using DelimiterFunc = AZStd::function<JsonPathValue(AZStd::string_view line)>;
DelimiterFunc m_delimiterFunc;
};
//! Merges a single command line argument into the settings registry. Command line arguments
@@ -322,6 +322,14 @@ namespace AZ
//! @return True if the registry folder was successfully merged, otherwise false.
virtual bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
AZStd::string_view platform = {}, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) = 0;
//! Stores the settings structure which is used when merging settings to the Settings Registry
//! using JSON Merge Patch or JSON Merge Patch.
//! The settings contain an issue reporting callback which can be used to track patching process.
//! Potential application of the reporting callback could be to update a UI whenever a key receives an updated value
//! @param applyPatchSettings The ApplyPatchSettings which are using during JSON Merging
virtual void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) = 0;
virtual void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) = 0;
};
inline SettingsRegistryInterface::Visitor::~Visitor() = default;
@@ -11,6 +11,7 @@
*/
#include <cctype>
#include <cerrno>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
@@ -419,9 +420,6 @@ namespace AZ
bool SettingsRegistryImpl::MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view rootKey,
const CommandLineArgumentSettings& commandLineSettings)
{
const char* front = argument.begin();
const char* back = argument.end();
if (!commandLineSettings.m_delimiterFunc)
{
AZ_Error("SettingsRegistry", false,
@@ -429,87 +427,40 @@ namespace AZ
aznumeric_cast<int>(argument.size()), argument.data());
return false;
}
const char* split = AZStd::find_if(front, back, commandLineSettings.m_delimiterFunc);
if (split == front || // There is no key
split == (back-1) || // There is no value
split == back) // Split character not found.
auto [key, value] = commandLineSettings.m_delimiterFunc(argument);
if (key.empty())
{
// They key where to set the JSON value cannot be empty
// The value of the JSON can be though
// This is so that a key can be set to empty string using "/KeyPath="
return false;
}
const char* keyStart = front;
while (std::isspace(*keyStart)) // This is safe because it will eventually stop on =
// Prepend the rootKey as an anchor to the argument key
SettingsRegistryInterface::FixedValueString keyPath{ rootKey.ends_with('/')
? rootKey.substr(0, rootKey.size() - 1)
: rootKey };
// Append the JSON reference token prefix of '/' to the keyPath
if (!key.starts_with('/'))
{
keyStart++;
keyPath.push_back('/');
}
if (keyStart == split) // Key is just white spaces
if ((key.size() + keyPath.size()) > keyPath.max_size())
{
// The key portion is longer than the FixedValueString max size that can be stored
// This limitation is arbitrary, if an AZStd::string is used or if the C++17 std::to_chars
// function is used, there wouldn't need to be a limitation
return false;
}
const char* keyEnd = split;
while (std::isspace(*--keyEnd));
keyEnd++;
keyPath += key;
key = keyPath;
char buffer[MaxJsonPathLength];
AZStd::string_view key;
bool keyHasDivider = *keyStart == '/';
if (!rootKey.empty())
if (value.empty())
{
bool rootKeyHasDivider = (rootKey[rootKey.length() - 1]) == '/';
size_t count;
if (!rootKeyHasDivider && !keyHasDivider)
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s/%.*s",
aznumeric_cast<int>(rootKey.length()), rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
else if (rootKeyHasDivider && keyHasDivider)
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s%.*s",
aznumeric_cast<int>(rootKey.length()) - 1, rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
else
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s%.*s",
aznumeric_cast<int>(rootKey.length()), rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
if (count >= AZ_ARRAY_SIZE(buffer) - 1)
{
return false;
}
key = AZStd::string_view(buffer, count);
}
else if (!keyHasDivider)
{
size_t count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "/%.*s",
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
if (count >= AZ_ARRAY_SIZE(buffer) - 1)
{
return false;
}
key = AZStd::string_view(buffer, count);
}
else
{
key = AZStd::string_view(keyStart, keyEnd);
return Set(key, value);
}
const char* valueStart = split + 1;
while (std::isspace(*valueStart) && valueStart < back)
{
valueStart++;
}
if (valueStart == back)
{
return false; // The value is empty
}
const char* valueEnd = back;
while (std::isspace(*(--valueEnd)));
valueEnd++;
AZStd::string_view value(valueStart, valueEnd);
if (value == "true")
{
return Set(key, true);
@@ -519,23 +470,35 @@ namespace AZ
return Set(key, false);
}
if (value.length() - 1 >= MaxCommandLineArgumentLength)
SettingsRegistryInterface::FixedValueString valueString;
if (value.size() > valueString.max_size())
{
// The value portion is longer than the FixedValueString max size that can be stored
// This limitation is arbitrary, if an AZStd::string is used or if the C++17 std::to_chars
// function is used, there wouldn't need to be a limitation
return false;
}
char argumentString[MaxCommandLineArgumentLength];
snprintf(argumentString, AZ_ARRAY_SIZE(argument), "%.*s", aznumeric_cast<int>(value.length()), value.data());
char* argumentStringEnd = argumentString + value.length();
valueString = value;
const char* valueStringEnd = valueString.c_str() + valueString.size();
errno = 0;
char* convertEnd = nullptr;
s64 intValue = strtoll(argumentString, &convertEnd, 0);
if (convertEnd == argumentStringEnd)
s64 intValue = strtoll(valueString.c_str(), &convertEnd, 0);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, intValue);
}
errno = 0;
convertEnd = nullptr;
double floatingPointValue = strtod(argumentString, &convertEnd);
if (convertEnd == argumentStringEnd)
u64 uintValue = strtoull(valueString.c_str(), &convertEnd, 0);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, uintValue);
}
errno = 0;
convertEnd = nullptr;
double floatingPointValue = strtod(valueString.c_str(), &convertEnd);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, floatingPointValue);
}
@@ -611,7 +574,7 @@ namespace AZ
}
else
{
if (MaxFilePathLength < path.length() + 1)
if (AZ::IO::MaxPathLength < path.length() + 1)
{
AZ_Error("Settings Registry", false,
R"(Path "%.*s" is too long. Either make sure that the provided path is terminated or use a shorter path.)",
@@ -623,10 +586,8 @@ namespace AZ
.AddMember(StringRef("Path"), AZStd::move(pathValue), m_settings.GetAllocator());
return false;
}
char filePath[MaxFilePathLength];
azstrncpy(filePath, AZ_ARRAY_SIZE(filePath), path.data(), path.length());
filePath[path.length()] = 0;
result = MergeSettingsFileInternal(filePath, format, rootKey, *scratchBuffer);
AZ::IO::FixedMaxPathString filePath(path);
result = MergeSettingsFileInternal(filePath.c_str(), format, rootKey, *scratchBuffer);
}
scratchBuffer->clear();
@@ -660,7 +621,7 @@ namespace AZ
additionalSpaceRequired += AZ_ARRAY_SIZE(PlatformFolder) + platform.length() + 2; // +2 for the two slashes.
}
if (path.length() + additionalSpaceRequired > MaxFilePathLength)
if (path.length() + additionalSpaceRequired > AZ::IO::MaxPathLength)
{
AZ_Error("Settings Registry", false, "Folder path for the Setting Registry is too long: %.*s",
static_cast<int>(path.size()), path.data());
@@ -673,7 +634,7 @@ namespace AZ
RegistryFileList fileList;
scratchBuffer->clear();
AZStd::fixed_string<MaxFilePathLength> folderPath{ path };
AZ::IO::FixedMaxPathString folderPath{ path };
constexpr AZStd::string_view pathSeparators{ AZ_CORRECT_AND_WRONG_DATABASE_SEPARATOR };
if (pathSeparators.find_first_of(folderPath.back()) == AZStd::string_view::npos)
{
@@ -926,7 +887,7 @@ namespace AZ
// Sort by the name first so the registry file gets applied with all its specializations.
if (lhs.m_tags[0] != rhs.m_tags[0])
{
return strcmp(lhs.m_relativePath, rhs.m_relativePath) < 0;
return lhs.m_relativePath < rhs.m_relativePath;
}
// Then sort by size first so the files with the fewest specializations get applied first.
@@ -956,14 +917,14 @@ namespace AZ
}
collisionFound = true;
AZ_Error("Settings Registry", false, R"(Two registry files point to the same specialization: "%s" and "%s")",
lhs.m_relativePath, rhs.m_relativePath);
AZ_Error("Settings Registry", false, R"(Two registry files in "%.*s" point to the same specialization: "%s" and "%s")",
AZ_STRING_ARG(folderPath), lhs.m_relativePath.c_str(), rhs.m_relativePath.c_str());
historyPointer.Create(m_settings, m_settings.GetAllocator()).SetObject()
.AddMember(StringRef("Error"), StringRef("Too many files in registry folder."), m_settings.GetAllocator())
.AddMember(StringRef("Path"),
Value(folderPath.data(), aznumeric_caster(folderPath.length()), m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File1"), Value(lhs.m_relativePath, m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File2"), Value(rhs.m_relativePath, m_settings.GetAllocator()), m_settings.GetAllocator());
.AddMember(StringRef("File1"), Value(lhs.m_relativePath.c_str(), m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File2"), Value(rhs.m_relativePath.c_str(), m_settings.GetAllocator()), m_settings.GetAllocator());
return false;
}
@@ -1036,9 +997,9 @@ namespace AZ
// thats the name tag.
AZStd::sort(AZStd::next(output.m_tags.begin()), output.m_tags.end());
if (filePathSize < AZ_ARRAY_SIZE(output.m_relativePath))
if (filePathSize < output.m_relativePath.max_size())
{
azstrcpy(output.m_relativePath, AZ_ARRAY_SIZE(output.m_relativePath), filename);
output.m_relativePath = filename;
return true;
}
else
@@ -1145,7 +1106,7 @@ namespace AZ
JsonSerializationResult::ResultCode mergeResult(JsonSerializationResult::Tasks::Merge);
if (rootKey.empty())
{
mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach);
mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
}
else
{
@@ -1153,7 +1114,7 @@ namespace AZ
if (root.IsValid())
{
Value& rootValue = root.Create(m_settings, m_settings.GetAllocator());
mergeResult = JsonSerialization::ApplyPatch(rootValue, m_settings.GetAllocator(), jsonPatch, mergeApproach);
mergeResult = JsonSerialization::ApplyPatch(rootValue, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
}
else
{
@@ -1180,4 +1141,13 @@ namespace AZ
return true;
}
void SettingsRegistryImpl::SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings)
{
m_applyPatchSettings = applyPatchSettings;
}
void SettingsRegistryImpl::GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings)
{
applyPatchSettings = m_applyPatchSettings;
}
} // namespace AZ
@@ -14,6 +14,7 @@
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/pointer.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Settings/SettingsRegistry.h>
@@ -35,9 +36,6 @@ namespace AZ
AZ_CLASS_ALLOCATOR(SettingsRegistryImpl, AZ::OSAllocator, 0);
AZ_RTTI(AZ::SettingsRegistryImpl, "{E9C34190-F888-48CA-83C9-9F24B4E21D72}", AZ::SettingsRegistryInterface);
static constexpr size_t MaxFilePathLength = AZ_MAX_PATH_LEN;
static constexpr size_t MaxJsonPathLength = 1024;
static constexpr size_t MaxCommandLineArgumentLength = 1024;
static constexpr size_t MaxRegistryFolderEntries = 128;
SettingsRegistryImpl();
@@ -80,11 +78,14 @@ namespace AZ
bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
AZStd::string_view platform, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) override;
void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) override;
void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) override;
private:
using TagList = AZStd::fixed_vector<size_t, Specializations::MaxCount + 1>;
struct RegistryFile
{
char m_relativePath[MaxFilePathLength]{ 0 };
AZ::IO::FixedMaxPathString m_relativePath;
TagList m_tags;
bool m_isPatch{ false };
bool m_isPlatformFile{ false };
@@ -109,5 +110,6 @@ namespace AZ
rapidjson::Document m_settings;
JsonSerializerSettings m_serializationSettings;
JsonDeserializerSettings m_deserializationSettings;
JsonApplyPatchSettings m_applyPatchSettings;
};
} // namespace AZ
@@ -340,46 +340,6 @@ namespace AZ::SettingsRegistryMergeUtils
return sectionName;
}
// Encodes a key, value delimited line such that the entire "key" can be stored as a single
// JSON Pointer key by escaping the tilde(~) and forward slash(/)
template<size_t BufferSize>
static AZStd::fixed_string<BufferSize> EncodeLineForJsonPointer(AZStd::string_view token,
const AZ::SettingsRegistryInterface::CommandLineArgumentSettings::DelimiterFunc& delimiterFunc)
{
if (!delimiterFunc)
{
// Since the delimiter function is not valid, return the token unchanged
return AZStd::fixed_string<BufferSize>{ token };
}
// Iterate over the line and escape the '~' and '/' values
AZStd::fixed_string<BufferSize> encodedToken;
size_t chIndex = 0;
for (; chIndex < token.size(); ++chIndex)
{
const char ch = token[chIndex];
if (delimiterFunc(ch))
{
// If the delimiter is found, this indicates that the end of the key has been found
break;
}
switch (ch)
{
case '~':
encodedToken += "~0";
break;
case '/':
encodedToken += "~1";
break;
default:
encodedToken += ch;
}
}
// Copy over the rest of the post delimited line to the encoded token
encodedToken.append(token.data() + chIndex, token.data() + token.size());
return encodedToken;
}
void QuerySpecializationsFromRegistry(SettingsRegistryInterface& registry, SettingsRegistryInterface::Specializations& specializations)
{
// Append any specializations stored in the registry
@@ -499,14 +459,7 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
// Check if the "key" portion of the line has '~' or '/' as the SettingsRegistry uses JSON Pointer
// to set the "value" portion. Those characters need to be escaped with ~0 and ~1 respectively
// to allow them to be embedded in a single json key
// Iterate over the line and escape the '~' and '/' values
AZStd::fixed_string<ConfigBufferMaxSize> escapedLine = EncodeLineForJsonPointer<ConfigBufferMaxSize>(line,
configParserSettings.m_commandLineSettings.m_delimiterFunc);
registry.MergeCommandLineArgument(escapedLine, currentJsonPointerPath, configParserSettings.m_commandLineSettings);
registry.MergeCommandLineArgument(line, currentJsonPointerPath, configParserSettings.m_commandLineSettings);
// Skip past the newline character if found
frontIter = lineEndIter + (foundNewLine ? 1 : 0);
@@ -54,6 +54,9 @@ namespace AZ
MOCK_METHOD5(
MergeSettingsFolder,
bool(AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
MOCK_METHOD1(SetApplyPatchSettings, void(const JsonApplyPatchSettings&));
MOCK_METHOD1(GetApplyPatchSettings, void(JsonApplyPatchSettings&));
};
} // namespace AZ
@@ -13,24 +13,25 @@
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Utils/Utils.h>
namespace AZ
{
using namespace UnitTest;
AZ_CVAR(bool, testBool, false, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(char, testChar, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int8_t, testInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int16_t, testInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int32_t, testInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int64_t, testInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint8_t, testUInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, testBool, false, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(char, testChar, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int8_t, testInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int16_t, testInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int32_t, testInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(int64_t, testInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint8_t, testUInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint16_t, testUInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint32_t, testUInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(uint64_t, testUInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, testFloat, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(double, testDouble, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, testFloat, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(double, testDouble, 0, nullptr, ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, testString, "default", nullptr, ConsoleFunctorFlags::Null, "");
@@ -189,7 +190,7 @@ namespace AZ
TEST_F(ConsoleTests, CVar_GetSetTest_Vector2)
{
testVec2 = AZ::Vector2{ 0.0f, 0.0f};
testVec2 = AZ::Vector2{ 0.0f, 0.0f };
TestCVarHelper(testVec2, "testVec2", "testVec2 1 1", "testVec2 asdf", AZ::Vector2(100, 100), AZ::Vector2(0, 0), AZ::Vector2(1, 1));
}
@@ -350,3 +351,245 @@ namespace AZ
}
}
}
namespace ConsoleSettingsRegistryTests
{
//! ConfigFile MergeUtils Test
struct ConfigFileParams
{
AZStd::string_view m_testConfigFileName;
AZStd::string_view m_testConfigContents;
};
class ConsoleSettingsRegistryFixture
: public UnitTest::ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<ConfigFileParams>
{
public:
void SetUp() override
{
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
// Store off the old global settings registry to restore after each test
m_oldSettingsRegistry = AZ::SettingsRegistry::Get();
if (m_oldSettingsRegistry != nullptr)
{
AZ::SettingsRegistry::Unregister(m_oldSettingsRegistry);
}
AZ::SettingsRegistry::Register(m_registry.get());
// Create a TestFile in the Test Directory
m_testFolder = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "ConsoleTestFolder";
auto configFileParams = GetParam();
CreateTestFile(m_testFolder / configFileParams.m_testConfigFileName, configFileParams.m_testConfigContents);
}
void TearDown() override
{
// Remove the Test Directory
DeleteFolderRecursive(m_testFolder);
// Restore the old global settings registry
AZ::SettingsRegistry::Unregister(m_registry.get());
if (m_oldSettingsRegistry != nullptr)
{
AZ::SettingsRegistry::Register(m_oldSettingsRegistry);
m_oldSettingsRegistry = {};
}
m_registry.reset();
}
void TestClassFunc(const AZ::ConsoleCommandContainer& someStrings)
{
m_stringArgCount = someStrings.size();
}
AZ_CONSOLEFUNC(ConsoleSettingsRegistryFixture, TestClassFunc, AZ::ConsoleFunctorFlags::Null, "");
static void DeleteFolderRecursive(const AZ::IO::PathView& path)
{
auto callback = [&path](AZStd::string_view filename, bool isFile) -> bool
{
if (isFile)
{
auto filePath = AZ::IO::FixedMaxPath(path) / filename;
AZ::IO::SystemFile::Delete(filePath.c_str());
}
else
{
if (filename != "." && filename != "..")
{
auto folderPath = AZ::IO::FixedMaxPath(path) / filename;
DeleteFolderRecursive(folderPath);
}
}
return true;
};
auto searchPath = AZ::IO::FixedMaxPath(path) / "*";
AZ::IO::SystemFile::FindFiles(searchPath.c_str(), callback);
AZ::IO::SystemFile::DeleteDir(AZ::IO::FixedMaxPathString(path.Native()).c_str());
}
static bool CreateTestFile(const AZ::IO::FixedMaxPath& testPath, AZStd::string_view content)
{
AZ::IO::SystemFile file;
if (!file.Open(testPath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_Assert(false, "Unable to open test file for writing: %s", testPath.c_str());
return false;
}
if (file.Write(content.data(), content.size()) != content.size())
{
AZ_Assert(false, "Unable to write content to test file: %s", testPath.c_str());
return false;
}
return true;
}
protected:
size_t m_stringArgCount{};
AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_registry;
AZ::IO::FixedMaxPath m_testFolder;
private:
AZ::SettingsRegistryInterface* m_oldSettingsRegistry{};
};
static bool s_consoleFreeFunctionInvoked = false;
static void TestSettingsRegistryFreeFunc(const AZ::ConsoleCommandContainer& someStrings)
{
EXPECT_TRUE(someStrings.empty());
s_consoleFreeFunctionInvoked = true;
}
AZ_CONSOLEFREEFUNC(TestSettingsRegistryFreeFunc, AZ::ConsoleFunctorFlags::Null, "");
TEST_P(ConsoleSettingsRegistryFixture, Console_AbleToLoadSettingsFile_Successfully)
{
AZ::Console testConsole(*m_registry);
testConsole.LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
AZ::Interface<AZ::IConsole>::Register(&testConsole);
AZ_CVAR_SCOPED(int32_t, testInit, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "");
s_consoleFreeFunctionInvoked = false;
testInit = {};
AZ::testChar = {};
AZ::testBool = {};
AZ::testInt8 = {};
AZ::testInt16 = {};
AZ::testInt32 = {};
AZ::testInt64 = {};
AZ::testUInt8 = {};
AZ::testUInt16 = {};
AZ::testUInt32 = {};
AZ::testUInt64 = {};
AZ::testFloat= {};
AZ::testDouble = {};
AZ::testString = {};
auto configFileParams = GetParam();
auto testFilePath = m_testFolder / configFileParams.m_testConfigFileName;
EXPECT_TRUE(AZ::IO::SystemFile::Exists(testFilePath.c_str()));
testConsole.ExecuteConfigFile(testFilePath.Native());
EXPECT_TRUE(s_consoleFreeFunctionInvoked);
EXPECT_EQ(3, testInit);
EXPECT_TRUE(static_cast<bool>(AZ::testBool));
EXPECT_EQ('Q', AZ::testChar);
EXPECT_EQ(24, AZ::testInt8);
EXPECT_EQ(-32, AZ::testInt16);
EXPECT_EQ(41, AZ::testInt32);
EXPECT_EQ(-51, AZ::testInt64);
EXPECT_EQ(3, AZ::testUInt8);
EXPECT_EQ(5, AZ::testUInt16);
EXPECT_EQ(6, AZ::testUInt32);
EXPECT_EQ(0xFFFF'FFFF'FFFF'FFFF, AZ::testUInt64);
EXPECT_FLOAT_EQ(1.0f, AZ::testFloat);
EXPECT_DOUBLE_EQ(2, AZ::testDouble);
EXPECT_STREQ("Stable", static_cast<AZ::CVarFixedString>(AZ::testString).c_str());
EXPECT_EQ(3, m_stringArgCount);
AZ::Interface<AZ::IConsole>::Unregister(&testConsole);
}
static constexpr AZStd::string_view UserINIStyleContent =
R"(
testInit = 3
testBool true
testChar Q
testInt8 24
testInt16 -32
testInt32 41
testInt64 -51
testUInt8 3
testUInt16 5
testUInt32 6
testUInt64 18446744073709551615
testFloat 1.0
testDouble 2
testString Stable
ConsoleSettingsRegistryFixture.testClassFunc Foo Bar Baz
TestSettingsRegistryFreeFunc
)";
static constexpr AZStd::string_view UserJsonMergePatchContent =
R"(
{
"Amazon": {
"AzCore": {
"Runtime": {
"ConsoleCommands": {
"testInit": 3,
"testBool": true,
"testChar": "Q",
"testInt8": 24,
"testInt16": -32,
"testInt32": 41,
"testInt64": -51,
"testUInt8": 3,
"testUInt16": 5,
"testUInt32": 6,
"testUInt64": 18446744073709551615,
"testFloat": 1.0,
"testDouble": 2,
"testString": "Stable",
"ConsoleSettingsRegistryFixture.testClassFunc": "Foo Bar Baz",
"TestSettingsRegistryFreeFunc": ""
}
}
}
}
}
)";
static constexpr AZStd::string_view UserJsonPatchContent =
R"(
[
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInit", "value": 3 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testBool", "value": true },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testChar", "value": "Q" },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt8", "value": 24 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt16", "value": -32 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt32", "value": 41 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt64", "value": -51 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt8", "value": 3 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt16", "value": 5 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt32", "value": 6 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt64", "value": 18446744073709551615 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testFloat", "value": 1.0 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testDouble", "value": 2 },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testString", "value": "Stable" },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/ConsoleSettingsRegistryFixture.testClassFunc", "value": "Foo Bar Baz" },
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/TestSettingsRegistryFreeFunc", "value": "" }
]
)";
INSTANTIATE_TEST_CASE_P(
ExecuteCommandFromSettingsFile,
ConsoleSettingsRegistryFixture,
::testing::Values(
ConfigFileParams{"user.cfg", UserINIStyleContent},
ConfigFileParams{"user.setreg", UserJsonMergePatchContent},
ConfigFileParams{"user.setregpatch", UserJsonPatchContent}
)
);
}
@@ -55,7 +55,7 @@ namespace SettingsRegistryConsoleUtilsTests
{
constexpr const char* settingsKey = "/TestKey";
constexpr const char* expectedValue = "TestValue";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
EXPECT_TRUE(testConsole.PerformCommand(AZ::SettingsRegistryConsoleUtils::SettingsRegistrySet, { settingsKey, expectedValue }));
@@ -69,7 +69,7 @@ namespace SettingsRegistryConsoleUtilsTests
{
constexpr const char* settingsKey = "/TestKey";
constexpr const char* expectedValue = "TestValue";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
// Scopes the console functor handle so that it destructs and unregisters the console functors
{
@@ -89,7 +89,7 @@ namespace SettingsRegistryConsoleUtilsTests
constexpr const char* settingsKey2 = "/TestKey2";
constexpr const char* expectedValue = R"(TestValue)";
constexpr const char* expectedValue2 = R"(Hello World)";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
@@ -109,7 +109,7 @@ namespace SettingsRegistryConsoleUtilsTests
constexpr const char* settingsKey2 = "/TestKey2";
constexpr const char* expectedValue = R"(TestValue)";
constexpr const char* expectedValue2 = R"(Hello World)";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
// Add settings to settings registry
EXPECT_TRUE(m_registry->Set(settingsKey, expectedValue));
@@ -137,7 +137,7 @@ namespace SettingsRegistryConsoleUtilsTests
constexpr const char* settingsKey2 = "/TestKey2";
constexpr const char* expectedValue = R"(TestValue)";
constexpr const char* expectedValue2 = R"(Hello World)";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
@@ -195,7 +195,7 @@ namespace SettingsRegistryConsoleUtilsTests
constexpr const char* SettingsKey2 = "TestKey2";
constexpr const char* ExpectedValue = R"(TestValue)";
constexpr const char* ExpectedValue2 = R"(Hello World)";
AZ::Console testConsole;
AZ::Console testConsole(*m_registry);
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
@@ -1228,27 +1228,33 @@ namespace SettingsRegistryTests
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLong_ReturnsFalse)
{
AZStd::string argument = AZStd::string::format("Te%*cst=Value", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxJsonPathLength), ' ');
constexpr int LongKeySize = 1024;
AZStd::string argument = AZStd::string::format("Te%*cst=Value", LongKeySize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLongWithDivider_ReturnsFalse)
{
AZStd::string argument = AZStd::string::format("/Te%*cst=Value", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxJsonPathLength), ' ');
constexpr int LongKeySize = 1024;
AZStd::string argument = AZStd::string::format("/Te%*cst=Value", LongKeySize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, "/Path", {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsTooLong_ReturnsFalse)
{
AZStd::string argument = AZStd::string::format("Test=Val%*cue", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxCommandLineArgumentLength), ' ');
constexpr int LongValueSize = 1024;
AZStd::string argument = AZStd::string::format("Test=Val%*cue", LongValueSize, ' ');
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsFalse)
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsEmptyString)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
AZ::SettingsRegistryInterface::FixedValueString value;
EXPECT_TRUE(m_registry->Get(value, "/Test"));
EXPECT_TRUE(value.empty());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKey_ReturnsFalse)
@@ -1271,9 +1277,13 @@ namespace SettingsRegistryTests
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" =Value", {}, {}));
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsFalse)
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsEmptyString)
{
EXPECT_FALSE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Key"));
AZ::SettingsRegistryInterface::FixedValueString value;
EXPECT_TRUE(m_registry->Get(value, "/Key"));
EXPECT_TRUE(value.empty());
}
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndValueAreSpaces_ReturnsFalse)
@@ -1367,9 +1377,8 @@ namespace SettingsRegistryTests
TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubStringThatsTooLong_ReturnsFalse)
{
char path[AZ::SettingsRegistryImpl::MaxFilePathLength + 1];
memset(path, '1', sizeof(path));
AZStd::string_view subPath(path, AZ::SettingsRegistryImpl::MaxFilePathLength);
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, '1');
const AZStd::string_view subPath(path);
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
@@ -1719,8 +1728,7 @@ namespace SettingsRegistryTests
TEST_F(SettingsRegistryTest, MergeSettingsFolder_PathTooLong_ReportsErrorAndReturnsFalse)
{
char path[AZ::SettingsRegistryImpl::MaxFilePathLength + 1]{};
memset(path, 'a', AZ_ARRAY_SIZE(path));
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, 'a');
AZ_TEST_START_TRACE_SUPPRESSION;
bool result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {}, nullptr);
@@ -1741,7 +1749,7 @@ namespace SettingsRegistryTests
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
EXPECT_GT(::UnitTest::TestRunner::Instance().StopAssertTests(), 0);
EXPECT_FALSE(result);
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
@@ -1751,11 +1759,5 @@ namespace SettingsRegistryTests
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/Path"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/Error"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/Path"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/File1"));
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/File2"));
}
} // namespace SettingsRegistryTests
@@ -236,8 +236,6 @@ namespace AzFramework
// Archive classes relies on the FileIOBase DirectInstance to close
// files properly
m_directFileIO.reset();
// The AZ::Console skips destruction and always leaks to allow it to be used in static memory
}
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
@@ -22,7 +22,9 @@ namespace AzNetworking
AZ::HashValue32 HashSerializer::GetHash() const
{
// Just truncate the upper bits
return static_cast<AZ::HashValue32>(m_hash);
const AZ::HashValue32 lower = static_cast<AZ::HashValue32>(m_hash);
const AZ::HashValue32 upper = static_cast<AZ::HashValue32>(m_hash >> 32);
return lower ^ upper;
}
SerializerMode HashSerializer::GetSerializerMode() const
@@ -56,6 +56,6 @@ namespace AzNetworking
private:
AZ::HashValue64 m_hash;
AZ::HashValue64 m_hash = AZ::HashValue64{ 0 };
};
}
@@ -13,7 +13,7 @@
#pragma once
#include <Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Multiplayer
{
@@ -102,7 +102,8 @@ namespace Multiplayer
AZ::TimeMs m_lastInputReceivedTimeMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_lastCorrectionSentTimeMs = AZ::TimeMs{ 0 };
ClientInputId m_clientInputId = ClientInputId{ 0 };
ClientInputId m_clientInputId = ClientInputId{ 0 }; // Clients incrementing inputId
ClientInputId m_lastClientInputId = ClientInputId{ 0 }; // Last inputId processed by the server
ClientInputId m_lastCorrectionInputId = ClientInputId{ 0 };
ClientInputId m_lastMigratedInputId = ClientInputId{ 0 }; // Used to resend inputs that were queued during a migration event
HostFrameId m_serverMigrateFrameId = InvalidHostFrameId;
@@ -15,7 +15,8 @@
#include <AzCore/Component/Component.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/MultiplayerStats.h>
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/IMultiplayer.h>
@@ -62,11 +63,15 @@ namespace Multiplayer
//! @}
NetEntityId GetNetEntityId() const;
NetEntityRole GetNetEntityRole() const;
bool IsAuthority() const;
bool IsAutonomous() const;
bool IsServer() const;
bool IsClient() const;
ConstNetworkEntityHandle GetEntityHandle() const;
NetworkEntityHandle GetEntityHandle();
void MarkDirty();
virtual void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) = 0;
virtual NetComponentId GetNetComponentId() const = 0;
virtual bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole netEntityRole, NetworkEntityRpcMessage& rpcMessage) = 0;
@@ -14,7 +14,8 @@
#include <AzCore/Name/Name.h>
#include <AzCore/std/containers/unordered_map.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/NetworkInput/IMultiplayerComponentInput.h>
namespace Multiplayer
{
@@ -22,13 +23,15 @@ namespace Multiplayer
{
public:
using PropertyNameLookupFunction = AZStd::function<const char*(PropertyIndex index)>;
using RpcNameLookupFunction = AZStd::function<const char* (RpcIndex index)>;
using RpcNameLookupFunction = AZStd::function<const char*(RpcIndex index)>;
using AllocComponentInputFunction = AZStd::function<AZStd::unique_ptr<IMultiplayerComponentInput>()>;
struct ComponentData
{
AZ::Name m_gemName;
AZ::Name m_componentName;
PropertyNameLookupFunction m_componentPropertyNameLookupFunction;
RpcNameLookupFunction m_componentRpcNameLookupFunction;
AllocComponentInputFunction m_allocComponentInputFunction;
};
//! Registers a multiplayer component with the multiplayer system.
@@ -36,6 +39,11 @@ namespace Multiplayer
//! @return the NetComponentId assigned to this particular component
NetComponentId RegisterMultiplayerComponent(const ComponentData& componentData);
//! Allocates a new component input for the provided netComponentId.
//! @param netComponentId the NetComponentId to allocate a component input for
//! @return pointer to the allocated component input, caller assumes ownership
AZStd::unique_ptr<IMultiplayerComponentInput> AllocateComponentInput(NetComponentId netComponentId);
//! Returns the gem name associated with the provided NetComponentId.
//! @param netComponentId the NetComponentId to return the gem name of
//! @return the name of the gem that contains the requested component
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Math/Aabb.h>
namespace Multiplayer
@@ -47,9 +47,13 @@ namespace Multiplayer
//! @return the networkId for the entity that owns this controller
NetEntityId GetNetEntityId() const;
//! Returns the networkRole for the entity that owns this controller.
//! @return the networkRole for the entity that owns this controller
NetEntityRole GetNetEntityRole() const;
//! Returns true if this controller has authority.
//! @return boolean true if this controller has authority
bool IsAuthority() const;
//! Returns true if this controller has autonomy (can locally predict).
//! @return boolean true if this controller has autonomy
bool IsAutonomous() const;
//! Returns the raw AZ::Entity pointer for the entity that owns this controller.
//! @return the raw AZ::Entity pointer for the entity that owns this controller
@@ -20,10 +20,10 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <Multiplayer/ReplicationRecord.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/IMultiplayerComponentInput.h>
#include <Multiplayer/INetworkTime.h>
#include <Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkInput/IMultiplayerComponentInput.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <Multiplayer/MultiplayerTypes.h>
#include <AzCore/EBus/Event.h>
@@ -63,12 +63,17 @@ namespace Multiplayer
NetEntityRole GetNetEntityRole() const;
bool IsAuthority() const;
bool IsAutonomous() const;
bool IsServer() const;
bool IsClient() const;
bool HasController() const;
NetEntityId GetNetEntityId() const;
const PrefabEntityId& GetPrefabEntityId() const;
ConstNetworkEntityHandle GetEntityHandle() const;
NetworkEntityHandle GetEntityHandle();
void SetOwningConnectionId(AzNetworking::ConnectionId connectionId);
void SetAllowAutonomy(bool value);
MultiplayerComponentInputVector AllocateComponentInputs();
bool IsProcessingInput() const;
void CreateInput(NetworkInput& networkInput, float deltaTime);
@@ -155,6 +160,7 @@ namespace Multiplayer
bool m_isProcessingInput = false;
bool m_isMigrationDataValid = false;
bool m_needsToBeStopped = false;
bool m_allowAutonomy = false; // Set to true for the hosts controlled entity
friend class NetworkEntityManager;
friend class EntityReplicationManager;
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
namespace Multiplayer
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
namespace Multiplayer
{
@@ -15,8 +15,9 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/INetworkTime.h>
#include <Multiplayer/Components/MultiplayerComponentRegistry.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <Multiplayer/MultiplayerStats.h>
namespace AzNetworking
@@ -101,28 +102,6 @@ namespace Multiplayer
//! @return pointer to the network entity manager instance bound to this multiplayer instance
virtual INetworkEntityManager* GetNetworkEntityManager() = 0;
//! Returns the gem name associated with the provided component index.
//! @param netComponentId the componentId to return the gem name of
//! @return the name of the gem that contains the requested component
virtual const char* GetComponentGemName(NetComponentId netComponentId) const = 0;
//! Returns the component name associated with the provided component index.
//! @param netComponentId the componentId to return the component name of
//! @return the name of the component
virtual const char* GetComponentName(NetComponentId netComponentId) const = 0;
//! Returns the property name associated with the provided component index and property index.
//! @param netComponentId the component index to return the property name of
//! @param propertyIndex the index of the network property to return the property name of
//! @return the name of the network property
virtual const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const = 0;
//! Returns the Rpc name associated with the provided component index and rpc index.
//! @param netComponentId the componentId to return the property name of
//! @param rpcIndex the index of the rpc to return the rpc name of
//! @return the name of the requested rpc
virtual const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const = 0;
//! Retrieve the stats object bound to this multiplayer instance.
//! @return the stats object bound to this multiplayer instance
MultiplayerStats& GetStats() { return m_stats; }
@@ -1,18 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
namespace Multiplayer
{
}
@@ -13,7 +13,7 @@
#pragma once
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/Asset/AssetCommon.h>
@@ -138,4 +138,4 @@ namespace Multiplayer
};
}
#include <Multiplayer/NetworkEntityHandle.inl>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.inl>
@@ -28,8 +28,9 @@ namespace Multiplayer
{
public:
virtual ~IMultiplayerComponentInput() = default;
virtual NetComponentId GetComponentId() const = 0;
virtual NetComponentId GetNetComponentId() const = 0;
virtual bool Serialize(AzNetworking::ISerializer& serializer) = 0;
virtual IMultiplayerComponentInput& operator= (const IMultiplayerComponentInput&) { return *this; }
};
using MultiplayerComponentInputVector = AZStd::vector<AZStd::unique_ptr<IMultiplayerComponentInput>>;
@@ -12,9 +12,9 @@
#pragma once
#include <Multiplayer/IMultiplayerComponentInput.h>
#include <Multiplayer/INetworkTime.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkInput/IMultiplayerComponentInput.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
namespace Multiplayer
@@ -57,15 +57,15 @@ namespace Multiplayer
IMultiplayerComponentInput* FindComponentInput(NetComponentId componentId);
template <class InputType>
const InputType* FindInput() const
const InputType* FindComponentInput() const
{
return static_cast<const InputType*>(FindInput(InputType::s_Type));
return static_cast<const InputType*>(FindComponentInput(InputType::s_netComponentId));
}
template <typename InputType>
InputType* FindInput()
InputType* FindComponentInput()
{
return static_cast<InputType*>(FindInput(InputType::s_Type));
return static_cast<InputType*>(FindComponentInput(InputType::s_netComponentId));
}
private:
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/INetworkTime.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/Utilities/NetworkCommon.h>
@@ -115,4 +115,4 @@ namespace AZ
AZ_TYPE_INFO_TEMPLATE(Multiplayer::RewindableObject, "{B2937B44-FEE1-4277-B1E0-863DE76D363F}", AZ_TYPE_INFO_TYPENAME, AZ_TYPE_INFO_AUTO);
}
#include <Multiplayer/RewindableObject.inl>
#include <Multiplayer/NetworkTime/RewindableObject.inl>
@@ -13,7 +13,7 @@
#pragma once
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/unordered_map.h>
namespace Multiplayer
@@ -1,6 +1,6 @@
#include <AzCore/Component/Component.h>
#include <Multiplayer/MultiplayerComponentRegistry.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/Components/MultiplayerComponentRegistry.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
{% for Component in dataFiles %}
{% set ComponentDerived = Component.attrib['OverrideComponent']|booleanTrue %}
{% set ControllerDerived = Component.attrib['OverrideController']|booleanTrue %}
@@ -38,7 +38,11 @@ namespace {{ Namespace }}
componentData.m_componentName = AZ::Name("{{ Component.attrib['Name'] }}");
componentData.m_componentPropertyNameLookupFunction = {{ ComponentBaseName }}::GetNetworkPropertyName;
componentData.m_componentRpcNameLookupFunction = {{ ComponentBaseName }}::GetRpcName;
componentData.m_allocComponentInputFunction = {{ ComponentBaseName }}::AllocateComponentInput;
{{ ComponentBaseName }}::s_netComponentId = multiplayerComponentRegistry->RegisterMultiplayerComponent(componentData);
{% if NetworkInputCount > 0 %}
{{ ComponentName }}NetworkInput::s_netComponentId = {{ ComponentBaseName }}::s_netComponentId;
{% endif %}
stats.ReserveComponentStats({{ ComponentBaseName }}::s_netComponentId, static_cast<uint16_t>({{ NetworkPropertyCount }}), static_cast<uint16_t>({{ RpcCount }}));
}
{% endfor %}
@@ -207,11 +207,11 @@ namespace {{ Component.attrib['Namespace'] }}
public:
AZ_MULTIPLAYER_COMPONENT({{ Component.attrib['Namespace'] }}::{{ ComponentName }}, s_{{ LowerFirst(ComponentName) }}ConcreteUuid, {{ Component.attrib['Namespace'] }}::{{ ComponentNameBase }});
static void Reflect([[maybe_unused]] AZ::ReflectContext* context);
static void Reflect(AZ::ReflectContext* context);
void OnInit() override {}
void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnInit() override;
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
{{ DeclareRpcHandlers(Component, 'Authority', 'Client', true)|indent(8) }}
};
@@ -222,15 +222,15 @@ namespace {{ Component.attrib['Namespace'] }}
: public {{ ControllerNameBase }}
{
public:
{{ ControllerName }}({{ ComponentName }}& parent) : {{ ControllerNameBase }}(parent) {}
{{ ControllerName }}({{ ComponentName }}& parent);
void OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) override {}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
{% if NetworkInputCount > 0 %}
//! Common input processing logic for the NetworkInput.
//! @param input input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
void ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {}
void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override;
{%endif %}
{{ DeclareRpcHandlers(Component, 'Server', 'Authority', true)|indent(8) }}
{{ DeclareRpcHandlers(Component, 'Client', 'Authority', true)|indent(8) }}
@@ -239,10 +239,12 @@ namespace {{ Component.attrib['Namespace'] }}
};
{% endif %}
}
{% if ComponentDerived %}
/// Place in your .cpp
#include <{{ Component.attrib['OverrideInclude'] }}>
namespace {{ Component.attrib['Namespace'] }}
{
{% if ComponentDerived %}
void {{ ComponentName }}::{{ ComponentName }}::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@@ -251,9 +253,43 @@ namespace {{ Component.attrib['Namespace'] }}
serializeContext->Class<{{ ComponentName }}, {{ ComponentNameBase }}>()
->Version(1);
}
{{ ComponentNameBase }}::Reflect(context);
}
}
void {{ ComponentName }}::OnInit()
{
}
void {{ ComponentName }}::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
}
void {{ ComponentName }}::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
}
{% endif %}
{% if ControllerDerived %}
{{ ControllerName }}::{{ ControllerName }}({{ ComponentName }}& parent)
: {{ ControllerNameBase }}(parent)
{
}
void {{ ControllerName }}::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
}
void {{ ControllerName }}::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
}
{% if NetworkInputCount > 0 %}
void {{ ControllerName }}::ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
{
}
{% endif %}
{% endif %}
}
*/
{% else %}
// NOTE:
@@ -7,25 +7,25 @@
{% macro DeclareNetworkPropertyGetter(Property) %}
{% set PropertyName = UpperFirst(Property.attrib['Name']) %}
{% if Property.attrib['Container'] == 'Array' %}
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
void {{ PropertyName }}AddEvent(AZ::Event<int32_t, {{ Property.attrib['Type'] }}>::Handler& handler);
{% endif %}
const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::k_RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const;
const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const;
{% elif Property.attrib['Container'] == 'Vector' %}
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
void {{ PropertyName }}AddEvent(AZ::Event<int32_t, {{ Property.attrib['Type'] }}>::Handler& handler);
void {{ PropertyName }}SizeChangedAddEvent(AZ::Event<uint32_t>::Handler& handler);
{% endif %}
{% elif Property.attrib['Container'] == 'Vector' %}
const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const;
const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const;
const {{ Property.attrib['Type'] }} &{{ PropertyName }}GetBack() const;
uint32_t {{ PropertyName }}GetSize() const;
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
void {{ PropertyName }}AddEvent(AZ::Event<int32_t, {{ Property.attrib['Type'] }}>::Handler& handler);
void {{ PropertyName }}SizeChangedAddEvent(AZ::Event<uint32_t>::Handler& handler);
{% endif %}
{% else %}
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const;
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
void {{ PropertyName }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler);
{% endif %}
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const;
{% endif %}
{% endmacro %}
{#
@@ -221,14 +221,14 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }
#include <AzCore/EBus/Event.h>
#include <AzCore/EBus/ScheduledEvent.h>
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
#include <Multiplayer/IMultiplayerComponentInput.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/MultiplayerController.h>
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/ReplicationRecord.h>
#include <Multiplayer/RewindableObject.h>
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/MultiplayerController.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h>
#include <Multiplayer/NetworkInput/IMultiplayerComponentInput.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Multiplayer/NetworkTime/RewindableObject.h>
{% call(Include) AutoComponentMacros.ParseIncludes(Component) %}
#include <{{ Include.attrib['File'] }}>
{% endcall %}
@@ -323,17 +323,20 @@ namespace {{ Component.attrib['Namespace'] }}
};
{% if NetworkInputCount > 0 %}
class NetworkInput
class {{ ComponentName }}NetworkInput
: public Multiplayer::IMultiplayerComponentInput
{
public:
Multiplayer::NetComponentId GetComponentId() const override;
INetworkInput& operator=(const INetworkInput& rhs) override;
bool Serialize(AzNetworking::ISerializer& serializer);
Multiplayer::NetComponentId GetNetComponentId() const override;
bool Serialize(AzNetworking::ISerializer& serializer) override;
Multiplayer::IMultiplayerComponentInput& operator =(const Multiplayer::IMultiplayerComponentInput& rhs) override;
{% call(Input) AutoComponentMacros.ParseNetworkInputs(Component) %}
{{ Input.attrib['Type'] }} m_{{ LowerFirst(Input.attrib['Name']) }} = {{ Input.attrib['Type'] }}({{ Input.attrib['Init'] }});
{% endcall %}
static Multiplayer::NetComponentId s_netComponentId;
friend void RegisterMultiplayerComponents();
};
{% endif %}
@@ -416,6 +419,8 @@ namespace {{ Component.attrib['Namespace'] }}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static AZStd::unique_ptr<Multiplayer::IMultiplayerComponentInput> AllocateComponentInput();
{{ ComponentBaseName }}() = default;
~{{ ComponentBaseName }}() override = default;
@@ -429,12 +434,14 @@ namespace {{ Component.attrib['Namespace'] }}
{% endif %}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', false)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', false)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) }}
{{ DeclareArchetypePropertyGetters(Component)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) }}
//! MultiplayerComponent interface
//! @{
void SetOwningConnectionId(AzNetworking::ConnectionId connectionId) override;
Multiplayer::NetComponentId GetNetComponentId() const override;
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& rpcMessage) override;
bool SerializeStateDeltaMessage(Multiplayer::ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) override;
@@ -515,7 +522,7 @@ namespace {{ Component.attrib['Namespace'] }}
//! Archetype Properties
{{ DeclareArchetypePropertyVars(Component)|indent(8) }}
{% call(Type, Name) AutoComponentMacros.ParseComponentServiceTypeAndName(Component) %}
{{ Type }}* {{ Name }} = nullptr;
{{ Type }}* {{ Name }} = nullptr;
{% endcall %}
static Multiplayer::NetComponentId s_netComponentId;
@@ -476,7 +476,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
MultiplayerStats& stats = GetMultiplayer()->GetStats();
Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats();
// We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server)
[[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject;
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %}
@@ -492,9 +492,9 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
if (deltaRecord.AnySet())
{
{% if Property.attrib['Container'] == 'Vector' %}
NovaNet::SerializableFixedSizeVectorDeltaStruct<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ LowerFirst(Property.attrib['Name']) }}, deltaRecord);
Multiplayer::SerializableFixedSizeVectorDeltaStruct<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ LowerFirst(Property.attrib['Name']) }}, deltaRecord);
{% else %}
NovaNet::SerializableFixedSizeArrayDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord);
Multiplayer::SerializableFixedSizeArrayDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord);
{% endif %}
serializer.Serialize(deltaStruct, "{{ UpperFirst(Property.attrib['Name']) }}");
}
@@ -509,7 +509,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
m_{{ LowerFirst(Property.attrib['Name']) }},
"{{ Property.attrib['Name'] }}",
GetNetComponentId(),
static_cast<PropertyIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(Property.attrib['Name']) }}),
static_cast<Multiplayer::PropertyIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(Property.attrib['Name']) }}),
stats
);
{% endif %}
@@ -958,8 +958,8 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Component/Entity.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
{% if ComponentDerived or ControllerDerived %}
#include <{{ Component.attrib['OverrideInclude'] }}>
{% endif %}
@@ -972,6 +972,9 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
namespace {{ Component.attrib['Namespace'] }}
{
Multiplayer::NetComponentId {{ UpperFirst(ComponentBaseName) }}::s_netComponentId = Multiplayer::InvalidNetComponentId;
{% if NetworkInputCount > 0 %}
Multiplayer::NetComponentId {{ ComponentName }}NetworkInput::s_netComponentId = Multiplayer::InvalidNetComponentId;
{% endif %}
namespace {{ UpperFirst(Component.attrib['Name']) }}Internal
{
@@ -1107,6 +1110,28 @@ namespace {{ Component.attrib['Namespace'] }}
{{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Autonomous', 'Authority')|indent(8) }}
}
{% if NetworkInputCount > 0 %}
Multiplayer::NetComponentId {{ ComponentName }}NetworkInput::GetNetComponentId() const
{
return {{ ComponentName }}NetworkInput::s_netComponentId;
}
bool {{ ComponentName }}NetworkInput::Serialize(AzNetworking::ISerializer& serializer)
{
{% call(Input) AutoComponentMacros.ParseNetworkInputs(Component) %}
serializer.Serialize(m_{{ LowerFirst(Input.attrib['Name']) }}, "{{ UpperFirst(Input.attrib['Name']) }}");
{% endcall %}
return serializer.IsValid();
}
Multiplayer::IMultiplayerComponentInput& {{ ComponentName }}NetworkInput::operator =([[maybe_unused]] const Multiplayer::IMultiplayerComponentInput& rhs)
{
AZ_Assert(s_netComponentId == rhs.GetNetComponentId(), "AttachNetSystemComponent was not called on the owning NetworkInput");
*this = *static_cast<const {{ ComponentName }}NetworkInput*>(&rhs);
return *this;
}
{% endif %}
{{ ControllerBaseName }}::{{ ControllerBaseName }}({{ ComponentName }}& parent)
: MultiplayerController(parent)
{
@@ -1163,10 +1188,10 @@ namespace {{ Component.attrib['Namespace'] }}
{{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Client', true)|indent(4) }}
{% for Service in Component.iter('ComponentRelation') %}
{% if (Service.attrib['HasController']|booleanTrue) and (Service.attrib['Constraint'] != 'Incompatible') %}
{{ Service.attrib['Name'] }}Controller* {{ ControllerBaseName }}::Get{{ Service.attrib['Name'] }}Controller()
{{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}Controller* {{ ControllerBaseName }}::Get{{ Service.attrib['Name'] }}Controller()
{
MultiplayerComponent* controllerComponent = GetParent().Get{{ Service.attrib['Name'] }}();
return static_cast<{{ Service.attrib['Name'] }}Controller*>(controllerComponent->GetController());
Multiplayer::MultiplayerComponent* controllerComponent = GetParent().Get{{ Service.attrib['Name'] }}();
return static_cast<{{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}Controller*>(controllerComponent->GetController());
}
{% endif %}
@@ -1241,7 +1266,7 @@ namespace {{ Component.attrib['Namespace'] }}
void {{ ComponentBaseName }}::{{ ComponentBaseName }}::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("{{ ComponentName }}Service"));
provided.push_back(AZ_CRC_CE("{{ ComponentName }}"));
}
void {{ ComponentBaseName }}::{{ ComponentBaseName }}::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -1261,12 +1286,21 @@ namespace {{ Component.attrib['Namespace'] }}
void {{ ComponentBaseName }}::{{ ComponentBaseName }}::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("{{ ComponentName }}Service"));
incompatible.push_back(AZ_CRC_CE("{{ ComponentName }}"));
{% call(ComponentService) ParseComponentServiceNames(Component, ClassType, 'Incompatible') %}
incompatible.push_back(AZ_CRC_CE("{{ ComponentService }}"));
{% endcall %}
}
AZStd::unique_ptr<Multiplayer::IMultiplayerComponentInput> {{ ComponentBaseName }}::AllocateComponentInput()
{
{% if NetworkInputCount > 0 %}
return AZStd::make_unique<{{ ComponentName }}NetworkInput>();
{% else %}
return nullptr;
{% endif %}
}
void {{ ComponentBaseName }}::Init()
{
if (m_netBindComponent == nullptr)
@@ -1328,6 +1362,15 @@ namespace {{ Component.attrib['Namespace'] }}
{{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}}
{{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', true)|indent(4) }}
void {{ ComponentBaseName }}::SetOwningConnectionId([[maybe_unused]] AzNetworking::ConnectionId connectionId)
{
{% for Property in Component.iter('NetworkProperty') %}
{% if Property.attrib['IsRewindable']|booleanTrue %}
m_{{ LowerFirst(Property.attrib['Name']) }}.SetOwningConnectionId(connectionId);
{% endif %}
{% endfor %}
}
Multiplayer::NetComponentId {{ ComponentBaseName }}::GetNetComponentId() const
{
return s_netComponentId;
@@ -1485,6 +1528,7 @@ namespace {{ Component.attrib['Namespace'] }}
}
{% endif %}
{% endfor %}
const char* {{ ComponentBaseName }}::GetNetworkPropertyName([[maybe_unused]] Multiplayer::PropertyIndex propertyIndex)
{
{% if NetworkPropertyCount > 0 %}
@@ -1514,6 +1558,5 @@ namespace {{ Component.attrib['Namespace'] }}
{% endif %}
return "Unknown Rpc";
}
{% endfor %}
}
{% endfor %}
@@ -5,13 +5,13 @@
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="true"
OverrideInclude="Source/Components/LocalPredictionPlayerInputComponent.h"
OverrideInclude="Multiplayer/Components/LocalPredictionPlayerInputComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Source/Components/NetworkTransformComponent.h" />
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<Include File="Multiplayer/MultiplayerTypes.h"/>
<Include File="Multiplayer/NetworkInput.h"/>
<Include File="Multiplayer/NetworkInput/NetworkInput.h"/>
<Include File="Source/NetworkInput/NetworkInputArray.h"/>
<Include File="Source/NetworkInput/NetworkInputHistory.h"/>
<Include File="Source/NetworkInput/NetworkInputMigrationVector.h"/>
@@ -3,9 +3,9 @@
<PacketGroup Name="MultiplayerPackets" PacketStart="CorePackets::PacketType::MAX">
<Include File="AzNetworking/AutoGen/CorePackets.AutoPackets.h" />
<Include File="Multiplayer/MultiplayerTypes.h" />
<Include File="Multiplayer/INetworkTime.h" />
<Include File="Multiplayer/NetworkEntityRpcMessage.h" />
<Include File="Multiplayer/NetworkEntityUpdateMessage.h" />
<Include File="Multiplayer/NetworkTime/INetworkTime.h" />
<Include File="Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h" />
<Include File="Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h" />
<Packet Name="Connect" Desc="Client connection packet, on success the server will reply with an Accept">
<Member Type="uint16_t" Name="networkProtocolVersion" Init="0" />
@@ -5,7 +5,7 @@
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="true"
OverrideInclude="Source/Components/NetworkTransformComponent.h"
OverrideInclude="Multiplayer/Components/NetworkTransformComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Weak" HasController="false" Name="TransformComponent" Namespace="AzFramework" Include="AzFramework/Components/TransformComponent.h" />
@@ -10,7 +10,7 @@
*
*/
#include <Source/Components/LocalPredictionPlayerInputComponent.h>
#include <Multiplayer/Components/LocalPredictionPlayerInputComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzNetworking/Serialization/HashSerializer.h>
@@ -81,12 +81,7 @@ namespace Multiplayer
, m_migrateStartHandler([this](ClientInputId migratedInputId) { OnMigrateStart(migratedInputId); })
, m_migrateEndHandler([this]() { OnMigrateEnd(); })
{
if (GetNetEntityRole() == NetEntityRole::Autonomous)
{
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
parent.GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
parent.GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
}
;
}
void LocalPredictionPlayerInputComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -96,6 +91,13 @@ namespace Multiplayer
m_allowMigrateClientInput = true;
m_serverMigrateFrameId = GetNetworkTime()->GetHostFrameId();
}
if (IsAutonomous())
{
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
GetParent().GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
GetParent().GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
}
}
void LocalPredictionPlayerInputComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -111,73 +113,57 @@ namespace Multiplayer
[[maybe_unused]] const AzNetworking::PacketEncodingBuffer& clientState
)
{
// After receiving the first input from the client, start the update event to check for slow hacking
if (!m_updateBankedTimeEvent.IsScheduled())
{
m_updateBankedTimeEvent.Enqueue(sv_InputUpdateTimeMs, true);
}
if (invokingConnection == nullptr)
{
// Discard any input messages that were locally dispatched or sent by disconnected clients
return;
}
const ClientInputId clientInputId = inputArray[0].GetClientInputId();
if (clientInputId <= m_lastClientInputId)
{
AZLOG(NET_Prediction, "Discarding old or out of order move input (current: %u, received %u)",
aznumeric_cast<uint32_t>(m_lastClientInputId), aznumeric_cast<uint32_t>(clientInputId));
return;
}
// After receiving the first input from the client, start the update event to check for slow hacking
if (!m_updateBankedTimeEvent.IsScheduled())
{
m_updateBankedTimeEvent.Enqueue(sv_InputUpdateTimeMs, true);
}
const AZ::TimeMs currentTimeMs = AZ::GetElapsedTimeMs();
const double clientInputRateSec = static_cast<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
m_lastInputReceivedTimeMs = currentTimeMs;
// Keep track of last inputs received, also allows us to update frame ids
m_lastInputReceived = inputArray;
// Figure out which index from the input array we want
// we start at the oldest input that has not been processed
int32_t inputArrayIndex = -1;
for (int32_t i = NetworkInputArray::MaxElements - 1; i >= 0; --i)
{
// Find an input that is newer than the last one we processed
if (m_lastInputReceived[i].GetClientInputId() > GetLastInputId())
{
inputArrayIndex = i;
break;
}
}
if (inputArrayIndex < 0)
{
AZLOG
(
NET_Prediction,
"Discarding old or out of order move input (current: %u, received %u)",
aznumeric_cast<uint32_t>(GetLastInputId()),
aznumeric_cast<uint32_t>(m_lastInputReceived[0].GetClientInputId())
);
return;
}
bool lostInput = false;
if (GetLastInputId() < inputArray.GetPreviousInputId())
{
// last move id processed is older than the previous input id, we missed some input packets
lostInput = true;
}
SetLastInputId(m_lastInputReceived[0].GetClientInputId()); // Set this variable in case of migration
while (inputArrayIndex >= 0)
while (m_lastClientInputId < clientInputId)
{
NetworkInput& input = m_lastInputReceived[inputArrayIndex];
++m_lastClientInputId;
// Figure out which index from the input array we want
// If we have skipped an id, check if it was sent to us in the array. If we have lost too many, just use the oldest one in the array
const uint32_t deltaFrameId = aznumeric_cast<uint32_t>(clientInputId - m_lastClientInputId); // always >= 0 because of while loop check
const uint32_t inputArrayIdx = AZStd::min(deltaFrameId, NetworkInputArray::MaxElements - 1);
const bool lostInput = deltaFrameId >= NetworkInputArray::MaxElements; // For logging only
NetworkInput &input = m_lastInputReceived[inputArrayIdx];
input.SetClientInputId(m_lastClientInputId);
// Anticheat, if we're receiving too many inputs, and fall outside our variable latency input window
// Discard move input events, client may be speed hacking
if (m_clientBankedTime < sv_MaxBankTimeWindowSec)
{
m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary
{
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId());
GetNetBindComponent()->ProcessInput(input, static_cast<float>(clientInputRateSec));
}
if (lostInput)
{
AZLOG(NET_Prediction, "InputLost InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
@@ -191,7 +177,6 @@ namespace Multiplayer
{
AZLOG(NET_Prediction, "Dropped InputId=%u", aznumeric_cast<uint32_t>(input.GetClientInputId()));
}
--inputArrayIndex;
}
if (sv_EnableCorrections && (currentTimeMs - m_lastCorrectionSentTimeMs > sv_MinCorrectionTimeMs))
@@ -203,6 +188,14 @@ namespace Multiplayer
const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash();
AZLOG
(
NET_Prediction,
"Hash values for ProcessInput: client=%u, server=%u",
aznumeric_cast<uint32_t>(stateHash),
aznumeric_cast<uint32_t>(localAuthorityHash)
);
if (stateHash != localAuthorityHash)
{
// Produce correction for client
@@ -540,21 +533,15 @@ namespace Multiplayer
m_inputHistory.PopFront();
}
const size_t inputHistorySize = m_inputHistory.Size();
const int64_t inputHistorySize = aznumeric_cast<int64_t>(m_inputHistory.Size());
// Form the rest of the input array using the n most recent elements in the history buffer
// NOTE: inputArray[0] has already been initialized hence start at i = 1
for (uint32_t i = 1; i < NetworkInputArray::MaxElements; ++i)
for (int64_t i = 1; i < aznumeric_cast<int64_t>(NetworkInputArray::MaxElements); ++i)
{
if (i < inputHistorySize)
{
inputArray[i] = m_inputHistory[inputHistorySize - 1 - i];
}
else // History is too small?
{
// Plug in the most recent input
inputArray[i] = input;
}
// Clamp to oldest element if history is too small
const int64_t historyIndex = AZStd::max<int64_t>(inputHistorySize - 1 - i, 0);
inputArray[i] = m_inputHistory[historyIndex];
}
// Send the input to server (only when we are not migrating)
@@ -10,8 +10,8 @@
*
*/
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Multiplayer
@@ -46,9 +46,24 @@ namespace Multiplayer
return m_netBindComponent ? m_netBindComponent->GetNetEntityId() : InvalidNetEntityId;
}
NetEntityRole MultiplayerComponent::GetNetEntityRole() const
bool MultiplayerComponent::IsAuthority() const
{
return m_netBindComponent ? m_netBindComponent->GetNetEntityRole() : NetEntityRole::InvalidRole;
return m_netBindComponent ? m_netBindComponent->IsAuthority() : false;
}
bool MultiplayerComponent::IsAutonomous() const
{
return m_netBindComponent ? m_netBindComponent->IsAutonomous() : false;
}
bool MultiplayerComponent::IsServer() const
{
return m_netBindComponent ? m_netBindComponent->IsServer() : false;
}
bool MultiplayerComponent::IsClient() const
{
return m_netBindComponent ? m_netBindComponent->IsClient() : false;
}
ConstNetworkEntityHandle MultiplayerComponent::GetEntityHandle() const
@@ -10,7 +10,7 @@
*
*/
#include <Multiplayer/MultiplayerComponentRegistry.h>
#include <Multiplayer/Components/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
@@ -21,6 +21,12 @@ namespace Multiplayer
return netComponentId;
}
AZStd::unique_ptr<IMultiplayerComponentInput> MultiplayerComponentRegistry::AllocateComponentInput(NetComponentId netComponentId)
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return AZStd::move(componentData.m_allocComponentInputFunction());
}
const char* MultiplayerComponentRegistry::GetComponentGemName(NetComponentId netComponentId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
@@ -10,9 +10,9 @@
*
*/
#include <Multiplayer/MultiplayerController.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/MultiplayerController.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Multiplayer
{
@@ -27,9 +27,14 @@ namespace Multiplayer
return m_owner.GetNetEntityId();
}
NetEntityRole MultiplayerController::GetNetEntityRole() const
bool MultiplayerController::IsAuthority() const
{
return GetNetBindComponent()->GetNetEntityRole();
return GetNetBindComponent() ? GetNetBindComponent()->IsAuthority() : false;
}
bool MultiplayerController::IsAutonomous() const
{
return GetNetBindComponent() ? GetNetBindComponent()->IsAutonomous() : false;
}
AZ::Entity* MultiplayerController::GetEntity() const
@@ -10,13 +10,13 @@
*
*/
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/MultiplayerController.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/MultiplayerController.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
@@ -110,6 +110,22 @@ namespace Multiplayer
return (m_netEntityRole == NetEntityRole::Authority);
}
bool NetBindComponent::IsAutonomous() const
{
return (m_netEntityRole == NetEntityRole::Autonomous)
|| (m_netEntityRole == NetEntityRole::Authority) && m_allowAutonomy;
}
bool NetBindComponent::IsServer() const
{
return (m_netEntityRole == NetEntityRole::Server);
}
bool NetBindComponent::IsClient() const
{
return (m_netEntityRole == NetEntityRole::Client);
}
bool NetBindComponent::HasController() const
{
return (m_netEntityRole == NetEntityRole::Authority)
@@ -136,14 +152,29 @@ namespace Multiplayer
return m_netEntityHandle;
}
void NetBindComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId)
{
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
{
multiplayerComponent->SetOwningConnectionId(connectionId);
}
}
void NetBindComponent::SetAllowAutonomy(bool value)
{
// This flag allows a player host to autonomously control their player entity, even though the entity is in an authority role
m_allowAutonomy = value;
}
MultiplayerComponentInputVector NetBindComponent::AllocateComponentInputs()
{
MultiplayerComponentInputVector componentInputs;
const size_t multiplayerComponentSize = m_multiplayerInputComponentVector.size();
for (size_t i = 0; i < multiplayerComponentSize; ++i)
{
// TODO: ComponentInput factory, needs multiplayer component architecture and autogen
AZStd::unique_ptr<IMultiplayerComponentInput> componentInput = nullptr; // ComponentInputFactory(multiplayerComponent->GetComponentId());
const NetComponentId netComponentId = m_multiplayerInputComponentVector[i]->GetNetComponentId();
AZStd::unique_ptr<IMultiplayerComponentInput> componentInput = AZStd::move(GetMultiplayerComponentRegistry()->AllocateComponentInput(netComponentId));
if (componentInput != nullptr)
{
componentInputs.emplace_back(AZStd::move(componentInput));
@@ -10,7 +10,7 @@
*
*/
#include <Source/Components/NetworkTransformComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/EBus/IEventScheduler.h>
@@ -96,7 +96,7 @@ namespace Multiplayer
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm)
{
if (GetNetEntityRole() == NetEntityRole::Authority)
if (IsAuthority())
{
SetRotation(worldTm.GetRotation());
SetTranslation(worldTm.GetTranslation());
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/IConnectionData.h>
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/IConnectionData.h>
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
@@ -138,7 +138,7 @@ namespace Multiplayer
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
{
const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
float callsPerSecond = 0.0f;
@@ -150,7 +150,7 @@ namespace Multiplayer
for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesSent.size(); ++index)
{
const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
const char* propertyName = multiplayer->GetComponentPropertyName(netComponentId, propertyIndex);
const char* propertyName = componentRegistry->GetComponentPropertyName(netComponentId, propertyIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesSent[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
@@ -172,7 +172,7 @@ namespace Multiplayer
for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesRecv.size(); ++index)
{
const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
const char* propertyName = multiplayer->GetComponentPropertyName(netComponentId, propertyIndex);
const char* propertyName = componentRegistry->GetComponentPropertyName(netComponentId, propertyIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesRecv[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
@@ -194,7 +194,7 @@ namespace Multiplayer
for (AZStd::size_t index = 0; index < componentStats.m_rpcsSent.size(); ++index)
{
const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
const char* rpcName = multiplayer->GetComponentRpcName(netComponentId, rpcIndex);
const char* rpcName = componentRegistry->GetComponentRpcName(netComponentId, rpcIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsSent[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
@@ -216,7 +216,7 @@ namespace Multiplayer
for (AZStd::size_t index = 0; index < componentStats.m_rpcsRecv.size(); ++index)
{
const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
const char* rpcName = multiplayer->GetComponentRpcName(netComponentId, rpcIndex);
const char* rpcName = componentRegistry->GetComponentRpcName(netComponentId, rpcIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsRecv[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
@@ -238,6 +238,7 @@ namespace Multiplayer
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None))
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
@@ -267,8 +268,8 @@ namespace Multiplayer
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
using StringLabel = AZStd::fixed_string<128>;
const StringLabel gemName = multiplayer->GetComponentGemName(netComponentId);
const StringLabel componentName = multiplayer->GetComponentName(netComponentId);
const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId);
const StringLabel componentName = componentRegistry->GetComponentName(netComponentId);
const StringLabel label = gemName + "::" + componentName;
if (DrawComponentRow(label.c_str(), stats, netComponentId))
{
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/IEntityDomain.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
namespace Multiplayer
{
@@ -16,7 +16,7 @@
#include <Source/AutoGen/AutoComponentTypes.h>
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
namespace Multiplayer
@@ -18,7 +18,7 @@
#include <Source/ReplicationWindows/NullReplicationWindow.h>
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
#include <Source/EntityDomains/FullOwnershipEntityDomain.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <AzNetworking/Framework/INetworking.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
@@ -473,6 +473,7 @@ namespace Multiplayer
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
}
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
@@ -601,26 +602,6 @@ namespace Multiplayer
return &m_networkEntityManager;
}
const char* MultiplayerSystemComponent::GetComponentGemName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentGemName(netComponentId);
}
const char* MultiplayerSystemComponent::GetComponentName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentName(netComponentId);
}
const char* MultiplayerSystemComponent::GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const
{
return GetMultiplayerComponentRegistry()->GetComponentPropertyName(netComponentId, propertyIndex);
}
const char* MultiplayerSystemComponent::GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const
{
return GetMultiplayerComponentRegistry()->GetComponentRpcName(netComponentId, rpcIndex);
}
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const MultiplayerStats& stats = GetStats();
@@ -96,10 +96,6 @@ namespace Multiplayer
AZ::TimeMs GetCurrentHostTimeMs() const override;
INetworkTime* GetNetworkTime() override;
INetworkEntityManager* GetNetworkEntityManager() override;
const char* GetComponentGemName(NetComponentId netComponentId) const override;
const char* GetComponentName(NetComponentId netComponentId) const override;
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const override;
const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const override;
//! @}
//! Console commands.
@@ -15,13 +15,13 @@
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/IEntityDomain.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/IReplicationWindow.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
@@ -828,12 +828,11 @@ namespace Multiplayer
{
if (entityReplicator == nullptr)
{
IMultiplayer* multiplayer = GetMultiplayer();
AZLOG_INFO
(
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted",
multiplayer->GetComponentName(message.GetComponentId()),
multiplayer->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()),
GetMultiplayerComponentRegistry()->GetComponentName(message.GetComponentId()),
GetMultiplayerComponentRegistry()->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()),
message.GetEntityId()
);
return false;
@@ -13,11 +13,11 @@
#pragma once
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/IReplicationWindow.h>
#include <Multiplayer/IEntityDomain.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/DataStructures/TimeoutQueue.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
#include <AzCore/std/containers/map.h>
@@ -16,11 +16,11 @@
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/Components/NetworkTransformComponent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <AzNetworking/PacketLayer/IPacket.h>
#include <AzNetworking/Serialization/ISerializer.h>
@@ -18,8 +18,8 @@
#include <AzCore/Component/EntityBus.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkEntityUpdateMessage.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
namespace AzNetworking
{
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/std/containers/ring_buffer.h>
namespace AzNetworking
@@ -12,7 +12,7 @@
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Multiplayer
{
@@ -10,7 +10,7 @@
*
*/
#include <Multiplayer/ReplicationRecord.h>
#include <Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h>
namespace Multiplayer
{
@@ -11,8 +11,8 @@
*/
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzNetworking/Utilities/NetworkCommon.h>
@@ -10,10 +10,10 @@
*
*/
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/MultiplayerController.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/Components/MultiplayerController.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -22,7 +22,7 @@
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Pipeline/NetworkSpawnableHolderComponent.h>
namespace Multiplayer
@@ -18,10 +18,10 @@
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
#include <Multiplayer/IEntityDomain.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/MultiplayerComponentRegistry.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/Components/MultiplayerComponentRegistry.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
namespace Multiplayer
{
@@ -10,7 +10,7 @@
*
*/
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzCore/Console/ILogger.h>
@@ -11,7 +11,7 @@
*/
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -13,7 +13,7 @@
#pragma once
#include <Multiplayer/MultiplayerTypes.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Component/Entity.h>
@@ -10,7 +10,7 @@
*
*/
#include <Multiplayer/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzCore/Console/ILogger.h>
@@ -10,8 +10,10 @@
*
*/
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Multiplayer/Components/MultiplayerComponentRegistry.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/IMultiplayer.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -111,13 +113,12 @@ namespace Multiplayer
// This happens when deserializing a non-delta'd input command
// However in the delta serializer case, we use the previous input as our initial value
// which will have the NetworkInputs setup and therefore won't write out the componentId
NetComponentId componentId = m_componentInputs[i] ? m_componentInputs[i]->GetComponentId() : InvalidNetComponentId;
NetComponentId componentId = m_componentInputs[i] ? m_componentInputs[i]->GetNetComponentId() : InvalidNetComponentId;
serializer.Serialize(componentId, "ComponentType");
// Create a new input if we don't have one or the types do not match
if ((m_componentInputs[i] == nullptr) || (componentId != m_componentInputs[i]->GetComponentId()))
if ((m_componentInputs[i] == nullptr) || (componentId != m_componentInputs[i]->GetNetComponentId()))
{
// TODO: ComponentInput factory, needs multiplayer component architecture and autogen
m_componentInputs[i] = nullptr; // ComponentInputFactory(componentId);
m_componentInputs[i] = AZStd::move(GetMultiplayerComponentRegistry()->AllocateComponentInput(componentId));
}
if (!m_componentInputs[i])
{
@@ -135,7 +136,7 @@ namespace Multiplayer
// We assume that the order of the network inputs is fixed between the server and client
for (auto& componentInput : m_componentInputs)
{
NetComponentId componentId = componentInput->GetComponentId();
NetComponentId componentId = componentInput->GetNetComponentId();
serializer.Serialize(componentId, "ComponentId");
serializer.Serialize(*componentInput, "ComponentInput");
}
@@ -148,7 +149,7 @@ namespace Multiplayer
// linear search since we expect to have very few components
for (auto& componentInput : m_componentInputs)
{
if (componentInput->GetComponentId() == componentId)
if (componentInput->GetNetComponentId() == componentId)
{
return componentInput.get();
}
@@ -165,16 +166,17 @@ namespace Multiplayer
void NetworkInput::CopyInternal(const NetworkInput& rhs)
{
m_inputId = rhs.m_inputId;
m_hostFrameId = rhs.m_hostFrameId;
m_hostTimeMs = rhs.m_hostTimeMs;
m_componentInputs.resize(rhs.m_componentInputs.size());
for (int32_t i = 0; i < rhs.m_componentInputs.size(); ++i)
{
if (m_componentInputs[i] == nullptr || m_componentInputs[i]->GetComponentId() != rhs.m_componentInputs[i]->GetComponentId())
const NetComponentId rhsComponentId = rhs.m_componentInputs[i]->GetNetComponentId();
if (m_componentInputs[i] == nullptr || m_componentInputs[i]->GetNetComponentId() != rhsComponentId)
{
// TODO: ComponentInput factory, needs multiplayer component architecture and autogen
m_componentInputs[i] = nullptr; // ComponentInputFactory(rhs.m_componentInputs[i]->GetComponentId());
m_componentInputs[i] = AZStd::move(GetMultiplayerComponentRegistry()->AllocateComponentInput(rhsComponentId));
}
*m_componentInputs[i] = *rhs.m_componentInputs[i];
*(m_componentInputs[i]) = *(rhs.m_componentInputs[i]);
}
m_wasAttached = rhs.m_wasAttached;
}
@@ -11,7 +11,7 @@
*/
#include <Source/NetworkInput/NetworkInputArray.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Serialization/DeltaSerializer.h>
@@ -48,16 +48,6 @@ namespace Multiplayer
return m_inputs[index].m_networkInput;
}
void NetworkInputArray::SetPreviousInputId(ClientInputId previousInputId)
{
m_previousInputId = previousInputId;
}
ClientInputId NetworkInputArray::GetPreviousInputId() const
{
return m_previousInputId;
}
bool NetworkInputArray::Serialize(AzNetworking::ISerializer& serializer)
{
// Always serialize the full first element
@@ -102,7 +92,6 @@ namespace Multiplayer
}
}
}
serializer.Serialize(m_previousInputId, "PreviousInputId");
return true;
}
}
@@ -12,8 +12,8 @@
#pragma once
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/fixed_vector.h>
@@ -33,9 +33,6 @@ namespace Multiplayer
NetworkInput& operator[](uint32_t index);
const NetworkInput& operator[](uint32_t index) const;
void SetPreviousInputId(ClientInputId previousInputId);
ClientInputId GetPreviousInputId() const;
bool Serialize(AzNetworking::ISerializer& serializer);
private:
@@ -49,6 +46,5 @@ namespace Multiplayer
ConstNetworkEntityHandle m_owner;
AZStd::array<Wrapper, MaxElements> m_inputs;
ClientInputId m_previousInputId;
};
}
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
namespace Multiplayer
{
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <AzCore/std/containers/deque.h>
namespace Multiplayer
@@ -12,8 +12,8 @@
#pragma once
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/fixed_vector.h>
@@ -11,8 +11,8 @@
*/
#include <Source/NetworkTime/NetworkTime.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
namespace Multiplayer
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/INetworkTime.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Console/IConsole.h>
@@ -19,7 +19,7 @@
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/Spawnable/SpawnableUtils.h>
#include <Multiplayer/IMultiplayerTools.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
@@ -12,7 +12,7 @@
#pragma once
#include <Multiplayer/IReplicationWindow.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
namespace Multiplayer
{
@@ -11,7 +11,7 @@
*/
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/ILogger.h>
@@ -13,8 +13,8 @@
#pragma once
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/IReplicationWindow.h>
#include <Multiplayer/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/EBus/ScheduledEvent.h>
@@ -10,8 +10,8 @@
*
*/
#include <Multiplayer/RewindableObject.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/NetworkTime/RewindableObject.h>
#include <Source/NetworkTime/NetworkTime.h>
#include <AzCore/Console/LoggerSystemComponent.h>
#include <AzCore/Time/TimeSystemComponent.h>
+21 -22
View File
@@ -10,35 +10,36 @@
#
set(FILES
Include/Multiplayer/IConnectionData.h
Include/Multiplayer/IEntityDomain.h
Include/Multiplayer/IMultiplayer.h
Include/Multiplayer/IMultiplayerComponentInput.h
Include/Multiplayer/IMultiplayerTools.h
Include/Multiplayer/INetworkEntityManager.h
Include/Multiplayer/INetworkPlayerSpawner.h
Include/Multiplayer/INetworkTime.h
Include/Multiplayer/IReplicationWindow.h
Include/Multiplayer/MultiplayerComponent.h
Include/Multiplayer/MultiplayerConstants.h
Include/Multiplayer/MultiplayerController.h
Include/Multiplayer/MultiplayerComponentRegistry.h
Include/Multiplayer/MultiplayerStats.cpp
Include/Multiplayer/MultiplayerStats.h
Include/Multiplayer/MultiplayerTypes.h
Include/Multiplayer/NetBindComponent.h
Include/Multiplayer/NetworkEntityRpcMessage.h
Include/Multiplayer/NetworkEntityUpdateMessage.h
Include/Multiplayer/NetworkEntityHandle.h
Include/Multiplayer/NetworkEntityHandle.inl
Include/Multiplayer/NetworkInput.h
Include/Multiplayer/ReplicationRecord.h
Include/Multiplayer/RewindableObject.h
Include/Multiplayer/RewindableObject.inl
Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h
Include/Multiplayer/Components/MultiplayerComponent.h
Include/Multiplayer/Components/MultiplayerController.h
Include/Multiplayer/Components/MultiplayerComponentRegistry.h
Include/Multiplayer/Components/NetBindComponent.h
Include/Multiplayer/Components/NetworkTransformComponent.h
Include/Multiplayer/ConnectionData/IConnectionData.h
Include/Multiplayer/EntityDomains/IEntityDomain.h
Include/Multiplayer/NetworkEntity/INetworkEntityManager.h
Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h
Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h
Include/Multiplayer/NetworkEntity/NetworkEntityHandle.h
Include/Multiplayer/NetworkEntity/NetworkEntityHandle.inl
Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h
Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h
Include/Multiplayer/NetworkInput/NetworkInput.h
Include/Multiplayer/NetworkTime/INetworkTime.h
Include/Multiplayer/NetworkTime/RewindableObject.h
Include/Multiplayer/NetworkTime/RewindableObject.inl
Include/Multiplayer/ReplicationWindows/IReplicationWindow.h
Source/Multiplayer_precompiled.cpp
Source/Multiplayer_precompiled.h
Source/MultiplayerSystemComponent.cpp
Source/MultiplayerSystemComponent.h
Source/MultiplayerStats.cpp
Source/AutoGen/AutoComponent_Header.jinja
Source/AutoGen/AutoComponent_Source.jinja
Source/AutoGen/AutoComponent_Common.jinja
@@ -49,13 +50,11 @@ set(FILES
Source/AutoGen/MultiplayerEditor.AutoPackets.xml
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
Source/Components/LocalPredictionPlayerInputComponent.cpp
Source/Components/LocalPredictionPlayerInputComponent.h
Source/Components/MultiplayerComponent.cpp
Source/Components/MultiplayerController.cpp
Source/Components/MultiplayerComponentRegistry.cpp
Source/Components/NetBindComponent.cpp
Source/Components/NetworkTransformComponent.cpp
Source/Components/NetworkTransformComponent.h
Source/ConnectionData/ClientToServerConnectionData.cpp
Source/ConnectionData/ClientToServerConnectionData.h
Source/ConnectionData/ClientToServerConnectionData.inl