diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h index 3d9259a256..892c63079d 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h @@ -23,6 +23,7 @@ namespace AzNetworking class ByteBuffer { public: + AZ_RTTI(ByteBuffer, "{CD6BFA48-290D-44B4-B376-2463F526BF1F}"); ByteBuffer() = default; ~ByteBuffer() = default; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 4279c26cf2..15223fba26 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -188,6 +188,63 @@ virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnect {% endmacro %} {# +#} +{% macro DeclareRpcEventGetter(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +AZ::Event<{{ ', '.join(paramTypes) }}>& Get{{ PropertyName }}Event() { return m_{{ PropertyName }}Event; } +{% endmacro %} +{# + +#} +{% macro DeclareRpcEventGetters(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcEventGetter(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + +#} +{% macro DeclareRpcEvent(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +AZ::Event<{{ ', '.join(paramTypes) }}> m_{{ PropertyName }}Event; +{% endmacro %} +{# + +#} +{% macro DeclareRpcEvents(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcEvent(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + +#} +{% macro DeclareRpcSignal(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +void Signal{{ PropertyName }}({{ ', '.join(paramDefines) }}); +{% endmacro %} +{# + +#} +{% macro DeclareRpcSignals(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcSignal(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + #} {%- macro EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentNameBase, ComponentDerived, ControllerName, ControllerNameBase, ControllerDerived, NetworkInputCount) -%} {% if ComponentDerived or ControllerDerived %} @@ -214,6 +271,10 @@ namespace {{ Component.attrib['Namespace'] }} void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; {{ DeclareRpcHandlers(Component, 'Authority', 'Client', true)|indent(8) }} + {{ DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) }} + protected: + {{ DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} }; {% endif %} @@ -236,6 +297,20 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareRpcHandlers(Component, 'Client', 'Authority', true)|indent(8) }} {{ DeclareRpcHandlers(Component, 'Autonomous', 'Authority', true)|indent(8) }} {{ DeclareRpcHandlers(Component, 'Authority', 'Autonomous', true)|indent(8) }} + {{ DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Authority', 'Autonomous')|indent(8) }} + + protected: + {{ DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Authority', 'Autonomous')|indent(8) }} }; {% endif %} } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 8ae8fee618..edc67a5da4 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -390,11 +390,25 @@ namespace {{ Component.attrib['Namespace'] }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Client', 'Authority', false)|indent(8) }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Autonomous', 'Authority', false)|indent(8) }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Autonomous', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Autonomous')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if (Service.attrib['HasController']|booleanTrue) and (Service.attrib['Constraint'] != 'Incompatible') %} {{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}Controller* Get{{ Service.attrib['Name'] }}Controller(); {% endif %} {% endfor %} + + protected: + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Autonomous')|indent(8) }} }; static const AZ::Uuid s_{{ LowerFirst(ComponentName) }}ConcreteUuid = "{{ (ComponentName) | createHashGuid }}"; @@ -438,6 +452,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) }} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) }} //! MultiplayerComponent interface //! @{ @@ -464,6 +479,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if Service.attrib['Constraint'] != 'Incompatible' %} const {{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}* Get{{ Service.attrib['Name'] }}() const; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 5e60510a5d..a9d2ecf3de 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -315,11 +315,25 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(par {% endmacro %} {# +#} +{% macro DefineRpcSignal(Component, ClassName, Property, InvokeFrom) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }}) +{ + m_{{ UpperFirst(Property.attrib['Name']) }}Event.Signal({{ ', '.join(paramNames) }}); +} +{% endmacro %} +{# + #} {% macro DefineRpcInvocations(Component, ClassName, InvokeFrom, HandleOn, ProctectedSection) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} {% if Property.attrib['IsPublic']|booleanTrue == ProctectedSection %} {{ DefineRpcInvocation(Component, ClassName, Property, InvokeFrom, HandleOn) }} +{{ DefineRpcSignal(Component, ClassName, Property, InvokeFrom) }} {% endif %} {% endcall %} {% endmacro %} @@ -341,6 +355,42 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(par {% endmacro %} {# +#} +{% macro ReflectRpcEventDescs(Component, ClassName, InvokeFrom, HandleOn) %} +{% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} + + // Create the BehaviorAZEventDescription needed to reflect the + // Get{{ UpperFirst(Property.attrib['Name']) }}Event method to the BehaviorContext without errors + AZ::BehaviorAzEventDescription {{ LowerFirst(Property.attrib['Name']) }}EventDesc; + {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_eventName = "{{ UpperFirst(Property.attrib['Name']) }} Notify Event"; + {% for Param in Property.iter('Param') %} + {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_parameterNames.push_back("{{ LowerFirst(Param.attrib['Name']) }}"); + {% endfor %} + +{% endcall %} +{% endmacro %} +{# + +#} +{% macro ReflectRpcEvents(Component, ClassName, InvokeFrom, HandleOn) %} +{% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} + ->Method("Get{{ UpperFirst(Property.attrib['Name']) }}Event", [](const {{ ClassName }}* self) -> AZ::Event<{{ ', '.join(paramTypes) }}>& + { + return self->m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); + }) + ->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move({{ LowerFirst(Property.attrib['Name']) }}EventDesc)) +{% endcall %} +{% endmacro %} +{# + #} {% macro DeclareRpcHandleCases(Component, ComponentDerived, InvokeFrom, HandleOn, ValidationFunction) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} @@ -363,6 +413,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Authority, "Entity proxy does not have authority"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); + m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); } {% if Property.attrib['IsReliable']|booleanTrue %} {# if the rpc is not reliable we can simply drop it, also note message reliability type is default reliable in EntityRpcMessage #} @@ -377,6 +428,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Autonomous, "Entity proxy does not have autonomy"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); + m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); } {% else %} Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); @@ -1302,6 +1354,11 @@ namespace {{ Component.attrib['Namespace'] }} AZ::BehaviorContext* behaviorContext = azrtti_cast(context); if (behaviorContext) { + {{ ReflectRpcEventDescs(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") @@ -1319,6 +1376,11 @@ namespace {{ Component.attrib['Namespace'] }} {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{- DefineArchetypePropertyBehaviorReflection(Component, ComponentName) | indent(16) }} ; } @@ -1418,7 +1480,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', true, ComponentBaseName)|indent(4) }} - {{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} +{{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', true)|indent(4) }} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 829fe7e495..523ffd90de 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -113,6 +113,10 @@ namespace Multiplayer { editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); } + if (auto console = AZ::Interface::Get(); console) + { + console->PerformCommand("disconnect"); + } break; } } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h index 293fb18928..6053040e08 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h @@ -24,6 +24,8 @@ namespace Multiplayer class NetworkInputArray final { public: + AZ_RTTI(NetworkInputArray, "{4908CE9F-8BCD-47C8-837F-09DC695ED2D7}"); + static constexpr uint32_t MaxElements = 8; // Never try to replicate a list larger than this amount NetworkInputArray(); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h index e5f8fdf648..f08bd023a0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h @@ -24,6 +24,7 @@ namespace Multiplayer class NetworkInputMigrationVector final { public: + AZ_RTTI(NetworkInputMigrationVector, "{BDF19B57-A11F-4185-9FA9-86AC12E67414}"); static constexpr uint32_t MaxElements = 90; // Never try to migrate a list larger than this amount, bumped up to handle DTLS connection time NetworkInputMigrationVector();