From c8b9f05079fd974d904504a63b77775b6e9d002c Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 9 Nov 2021 15:35:17 -0800 Subject: [PATCH 1/2] Ensuring the 4 types of RPCs (1)Autonomous->Authority (2)Authority->Autonomous (3)Authority->Client and (4)Server->Authority all compile. Invoking anything that starts from Autonomous or Authority requires a controller (otherwise just requires a component), and anything handled by Authority or Autonomous requires a controller for Handle on OnEvent methods (otherwise OnEvent and Handle occurs on the component) Signed-off-by: Gene Walters --- .../AutoGen/AutoComponent_Header.jinja | 28 +++---- .../AutoGen/AutoComponent_Source.jinja | 78 ++++++++++--------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja index 5cfeb250fa..b78b7cffba 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja @@ -443,35 +443,31 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} - {{ DeclareRpcInvocations(Component, 'Client', 'Authority', false)|indent(8) -}} - {{ DeclareRpcInvocations(Component, 'Client', 'Authority', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Autonomous', 'Authority', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Autonomous', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Client', true)|indent(8) -}} + + //! RPC Handlers: Override handlers in order ti implement what happens after receiving an RPC {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Server', 'Authority', false)|indent(8) -}} - {{ 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) -}} + + //! RPC Event Getters: Subscribe to these events and get notified when an RPC is received {{ 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) }} + {{ 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) }} }; @@ -517,6 +513,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', false)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} + + //! RPC Event Getters: Subscribe to these events and get notified when this component receives an RPC {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) -}} //! MultiplayerComponent interface @@ -541,9 +539,13 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} + + //! RPC Handlers: Override handlers in order to implement what happens after receiving an RPC {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) -}} - {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} + + //! RPC Events: Subscribe to these events and get notified when an RPC is received + {{ 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/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja index cf62f6f901..e9bc21875b 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja @@ -340,27 +340,11 @@ 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, IsProtected) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} {% if Property.attrib['IsPublic']|booleanTrue != IsProtected %} {{ DefineRpcInvocation(Component, ClassName, Property, InvokeFrom, HandleOn) -}} -{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} -{{ DefineRpcSignal(Component, ClassName, Property, InvokeFrom) -}} -{% endif %} {% endif %} {% endcall %} {% endmacro %} @@ -374,33 +358,46 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo {% set paramTypes = [] %} {% set paramDefines = [] %} {{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} - ->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) { - self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); + ->Method("{{ UpperFirst(Property.attrib['Name']) }}", []({{ ClassName }}* self, {{ ', '.join(paramDefines) }}) { +{% if (InvokeFrom == 'Server') %} + self->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); +{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %} + if (self->m_controller) + { + self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); + } + else + { + AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This remote-procedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", self->GetEntity()->GetName().c_str(), self->GetEntityId().ToString().c_str()) + } +{% endif %} }) ->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntityId", [](AZ::EntityId id, {{ ', '.join(paramDefines) }}) { AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); if (!entity) { - AZ_Warning("Network Property", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) return; } {{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>(); if (!networkComponent) { - AZ_Warning("Network Property", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) + AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) return; } - +{% if (InvokeFrom == 'Server') %} + networkComponent->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); +{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %} {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); if (!controller) { - AZ_Warning("Network Property", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) + AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) return; } - controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); +{% endif %} }, { { { "Source", "The Source containing the {{ ClassName }}Controller" }{% for paramName in paramNames %}, {"{{ paramName }}"}{% endfor %}}}) ->Attribute(AZ::Script::Attributes::ToolTip, "{{Property.attrib['Description']}}") {% endif %} @@ -436,9 +433,13 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo {% set paramTypes = [] %} {% set paramDefines = [] %} {{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} - ->Method("Get{{ UpperFirst(Property.attrib['Name']) }}Event", [](const {{ ClassName }}* self) -> AZ::Event<{{ ', '.join(paramTypes) }}>& + ->Method("Get{{ UpperFirst(Property.attrib['Name']) }}Event", []({{ ClassName }}* self) -> AZ::Event<{{ ', '.join(paramTypes) }}>& { +{% if HandleOn == 'Client' %} + return self->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); +{% elif (HandleOn == 'Authority') or (HandleOn == 'Autonomous') %} return self->m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); +{% endif %} }) ->Attribute(AZ::Script::Attributes::AzEventDescription, {{ LowerFirst(Property.attrib['Name']) }}EventDesc) ->Method("Get{{ UpperFirst(Property.attrib['Name']) }}EventByEntityId", [](AZ::EntityId id) -> AZ::Event<{{ ', '.join(paramTypes) }}>* @@ -456,7 +457,9 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }}EventByEntityId failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) return nullptr; } - +{% if HandleOn == 'Client' %} + return &networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); +{% elif (HandleOn == 'Authority') or (HandleOn == 'Autonomous') %} {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); if (!controller) { @@ -465,6 +468,7 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo } return &controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); +{% endif %} }) ->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move({{ LowerFirst(Property.attrib['Name']) }}EventDesc)) {% endif %} @@ -494,29 +498,31 @@ 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) }}); -{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} - m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); -{% endif %} +{% if (Property.attrib['GenerateEventBindings']|booleanTrue == true) %} + m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event().Signal({{ ', '.join(rpcParamList) }}); +{% endif %} } -{% 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 #} else // Note that this rpc is marked reliable, trigger the appropriate rpc event so it can be forwarded { +{% 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 #} m_netBindComponent->{{ "GetSend" + InvokeFrom + "To" + HandleOn + "RpcEvent" }}().Signal(message); +{% endif %} } - -{% endif %} {% elif HandleOn == 'Autonomous' %} if (m_controller) { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Autonomous, "Entity proxy does not have autonomy"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); -{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} - m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); -{% endif %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} + m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event().Signal({{ ', '.join(rpcParamList) }}); +{% endif %} } -{% else %} +{% elif HandleOn == 'Client' %} Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} + m_{{ UpperFirst(Property.attrib['Name']) }}Event.Signal({{ ', '.join(rpcParamList) }}); +{% endif %} {% endif %} } else if (paramsSerialized) From c99071533d2ff5acec2b576d3932c3635e5b6565 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 9 Nov 2021 15:39:43 -0800 Subject: [PATCH 2/2] Small: fix typo Signed-off-by: Gene Walters --- .../Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja index b78b7cffba..58a47b336f 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Header.jinja @@ -450,7 +450,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareRpcInvocations(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Client', true)|indent(8) -}} - //! RPC Handlers: Override handlers in order ti implement what happens after receiving an RPC + //! RPC Handlers: Override handlers in order to implement what happens after receiving an RPC {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Server', 'Authority', false)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Autonomous', 'Authority', false)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Autonomous', false)|indent(8) -}}