Network Input Exposed to Script (#3990)

* NetworkInput now has new attribute called ExposeToScript. NetworkInput with this attribute set to True will be exposed to behavior context. Also added a CreateFromValues for the NetworkInput where scripters can create an instance of the MyComponentNetworkInput class which will eventually be passed around CreateInput and ProcessInput events.

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Adding Create Input event handler. SC can now receive the event to create input, and send it over the network

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Auto-component controller will now generate CreateInput/ProcessInput methods if they have input exposed to script, not ready for use yet, just stubbed in

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Reducing code replication by putting common network input variables into AutoComponent_Common.jinja

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Fix minor comment typo in the CreateInput method

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Small fix. Changing ebus call from MyComponentNameCreateInput to just CreateInput. It's part of the MyComponentRequestBus so adding the component name before CreateInput is noisy

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Cleaning with jinja a bit using macro calls to iterate over scriptable netinputs

Signed-off-by: Gene Walters <genewalt@amazon.com>

* ProcessInput will now be triggered in script

Signed-off-by: Gene Walters <genewalt@amazon.com>

* ProcessInput event is sent to script. Script can now create and process input. Tested locally with a simple script.

Signed-off-by: Gene Walters <genewalt@amazon.com>

* Created a seperate CreateInputFromScript and ProcessInputFromScript. Developers no longer need to remember to call the BaseClass::CreateInput and ProcessInput since CreateInputFromScript will automatically be called beforehand.

Signed-off-by: Gene Walters <genewalt@amazon.com>
monroegm-disable-blank-issue-2
Gene Walters 4 years ago committed by GitHub
parent a91ea7d549
commit 73b04d7e34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -89,11 +89,23 @@ namespace Multiplayer
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void ProcessInput(NetworkInput& networkInput, float deltaTime) = 0;
//! Similar to ProcessInput, do not call directly.
//! This only needs to be overridden in components which allow NetworkInput to be processed by script.
//! @param networkInput input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void ProcessInputFromScript([[maybe_unused]] NetworkInput& networkInput, [[maybe_unused]] float deltaTime){}
//! Only valid on a client, should never be invoked on the server.
//! @param networkInput input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void CreateInput(NetworkInput& networkInput, float deltaTime) = 0;
//! Similar to CreateInput, should never be invoked on the server.
//! This only needs to be overridden in components which allow NetworkInput creation to be handled by scripts.
//! @param networkInput input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void CreateInputFromScript([[maybe_unused]]NetworkInput& networkInput, [[maybe_unused]] float deltaTime) {}
template <typename ComponentType>
const ComponentType* FindComponent() const;

@ -252,7 +252,44 @@ void Signal{{ PropertyName }}({{ ', '.join(paramDefines) }});
{#
#}
{%- macro EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentNameBase, ComponentDerived, ControllerName, ControllerNameBase, ControllerDerived, NetworkInputCount) -%}
{% macro GetNetworkInputCount(Component) -%}
{{ Component.findall('NetworkInput') | len }}
{%- endmacro -%}
{#
#}
{% macro ParseNetworkInputsExposedToScript(Component) -%}
{% set NetworkInputsExposedToScript = namespace(value=0) %}
{% for netInput in Component.findall('NetworkInput') %}
{% if ('ExposeToScript' in netInput.attrib) and (netInput.attrib['ExposeToScript'] |booleanTrue) %}
{{ caller(netInput) -}}
{% endif %}
{% endfor %}
{%- endmacro -%}
{#
#}
{% macro GetNetworkInputsExposedToScriptCount(Component) -%}
{% set NetworkInputsExposedToScript = namespace(value=0) %}
{% call (netInput) ParseNetworkInputsExposedToScript(Component) %}
{% set NetworkInputsExposedToScript.value = NetworkInputsExposedToScript.value + 1 %}
{% endcall %}
{{ NetworkInputsExposedToScript.value }}
{%- endmacro -%}
{#
#}
{% macro GetCommaSeparatedParamListOfScriptableNetworkInputs(Component) -%}
{% set parameters = [] %}
{% call (netInput) ParseNetworkInputsExposedToScript(Component) %}
{% set parameters = parameters.append(netInput.attrib['Type'] + ' ' + LowerFirst(netInput.attrib['Name'])) %}
{% endcall %}
{{ parameters | join(', ') }}
{%- endmacro -%}
{#
#}
{%- macro EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentNameBase, ComponentDerived, ControllerName, ControllerNameBase, ControllerDerived) -%}
{% if ComponentDerived or ControllerDerived %}
/*
/// You may use the classes below as a basis for your new derived classes. Derived classes must be marked in {{ (dataFileNames[0] | basename) }}
@ -293,7 +330,16 @@ namespace {{ Component.attrib['Namespace'] }}
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
{% set NetworkInputCount = GetNetworkInputCount(Component) | int %}
{% if NetworkInputCount > 0 %}
//! Common input creation logic for the NetworkInput.
//! Fill out the input struct and the MultiplayerInputDriver will send the input data over the network
//! to ensure it's processed.
//! @param input input structure which to store input data for sending to the authority
//! @param deltaTime amount of time to integrate the provided inputs over
void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override;
//! Common input processing logic for the NetworkInput.
//! @param input input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
@ -366,6 +412,18 @@ namespace {{ Component.attrib['Namespace'] }}
{
}
{% if NetworkInputCount > 0 %}
{% set net_input_parameters_name = [] %}
{% call (netInput) ParseNetworkInputsExposedToScript(Component) %}
{% set net_input_parameters_name = net_input_parameters_name.append(LowerFirst(netInput.attrib['Name'])) %}
{% endcall %}
void {{ ControllerName }}::CreateInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
{
{% if (GetNetworkInputsExposedToScriptCount(Component) | int) > 0 %}
// Remember the following NetworkInputs have been exposed to script: {{ net_input_parameters_name|join(', ') }}.
// If a script is handling these inputs they will have already be filled out by now.
{% endif %}
}
void {{ ControllerName }}::ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
{

@ -230,7 +230,8 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }
{% if ControllerDerived %}
{% set ControllerBaseName = ControllerName + "Base" %}
{% endif %}
{% set NetworkInputCount = Component.findall('NetworkInput') | len %}
{% set NetworkInputCount = AutoComponentMacros.GetNetworkInputCount(Component) | int %}
{% set NetworkInputsExposedToScriptCount = AutoComponentMacros.GetNetworkInputsExposedToScriptCount(Component) | int %}
{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %}
{% set RpcCount = Component.findall('RemoteProcedure') | len %}
#include "AutoComponentTypes.h"
@ -250,6 +251,10 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }
{% call(Include) AutoComponentMacros.ParseIncludes(Component) %}
#include <{{ Include.attrib['File'] }}>
{% endcall %}
{% if NetworkInputsExposedToScriptCount > 0 %}
#include <AzCore/RTTI/BehaviorContext.h>
{% endif %}
{% for Service in Component.iter('ComponentRelation') %}
{% if Service.attrib['Constraint'] != 'Incompatible' %}
@ -263,7 +268,7 @@ namespace {{ Service.attrib['Namespace'] }}
{% endif %}
{% endfor %}
{{ AutoComponentMacros.EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentBaseName, ComponentDerived, ControllerName, ControllerBaseName, ControllerDerived, NetworkInputCount) }}
{{ AutoComponentMacros.EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentBaseName, ComponentDerived, ControllerName, ControllerBaseName, ControllerDerived) }}
namespace {{ Component.attrib['Namespace'] }}
{
//! Forward declarations
@ -337,6 +342,13 @@ namespace {{ Component.attrib['Namespace'] }}
: public Multiplayer::IMultiplayerComponentInput
{
public:
{% if NetworkInputsExposedToScriptCount > 0 %}
AZ_TYPE_INFO({{ ComponentName }}NetworkInput, "{{ (ComponentName ~ "NetworkInput") | createHashGuid }}")
{{ ComponentName }}NetworkInput() = default;
{{ ComponentName }}NetworkInput({{ AutoComponentMacros.GetCommaSeparatedParamListOfScriptableNetworkInputs(Component) }});
static void Reflect(AZ::ReflectContext* context);
{% endif%}
Multiplayer::NetComponentId GetNetComponentId() const override;
bool Serialize(AzNetworking::ISerializer& serializer) override;
Multiplayer::IMultiplayerComponentInput& operator =(const Multiplayer::IMultiplayerComponentInput& rhs) override;
@ -348,7 +360,41 @@ namespace {{ Component.attrib['Namespace'] }}
static Multiplayer::NetComponentId s_netComponentId;
friend void RegisterMultiplayerComponents();
};
{% if NetworkInputsExposedToScriptCount > 0 %}
class {{ ComponentName }}Requests
: public AZ::ComponentBus
{
public:
AZ_RTTI({{ ComponentName }}Requests, "{{ (ComponentName ~ "Requests") | createHashGuid }}")
virtual {{ ComponentName }}NetworkInput CreateInput(float deltaTime) = 0;
virtual void ProcessInput({{ ComponentName }}NetworkInput* networkInput, float deltaTime) = 0;
};
using {{ ComponentName }}RequestBus = AZ::EBus<{{ ComponentName }}Requests>;
class {{ ComponentName }}BusHandler final
: public {{ ComponentName }}RequestBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER({{ ComponentName }}BusHandler, "{{ (ComponentName ~ "BusHandler") | createHashGuid }}", AZ::SystemAllocator, CreateInput, ProcessInput)
{{ ComponentName }}NetworkInput CreateInput(float deltaTime) override
{
{{ ComponentName }}NetworkInput result;
CallResult(result, FN_CreateInput, deltaTime);
return result;
}
void ProcessInput({{ ComponentName }}NetworkInput* networkInput, float deltaTime) override
{
Call(FN_ProcessInput, networkInput, deltaTime);
}
};
{% endif %}
{% endif %}
class {{ ControllerBaseName }}{% if not ControllerDerived %} final{% endif %}{{ "" }}
: public Multiplayer::MultiplayerController
@ -373,8 +419,14 @@ namespace {{ Component.attrib['Namespace'] }}
//! MultiplayerController interface
//! @{
Multiplayer::MultiplayerController::InputPriorityOrder GetInputOrder() const override { return Multiplayer::MultiplayerController::InputPriorityOrder::Default; }
{% if NetworkInputsExposedToScriptCount > 0 %}
void CreateInputFromScript([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) final;
void ProcessInputFromScript([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) final;
{% endif %}
void CreateInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {}
void ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {}
//! @}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', false)|indent(8) -}}

@ -1152,7 +1152,8 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
{% else %}
{% set ControllerBaseName = ControllerName %}
{% endif %}
{% set NetworkInputCount = Component.findall('NetworkInput') | len %}
{% set NetworkInputCount = AutoComponentMacros.GetNetworkInputCount(Component) | int %}
{% set NetworkInputsExposedToScriptCount = AutoComponentMacros.GetNetworkInputsExposedToScriptCount(Component) | int %}
{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %}
{% set RpcCount = Component.findall('RemoteProcedure') | len %}
#include "{{ includeFile }}"
@ -1299,6 +1300,56 @@ namespace {{ Component.attrib['Namespace'] }}
}
{% if NetworkInputCount > 0 %}
{% set ScriptableNetworkInputParamNames = [] %}
{% call(netInput) AutoComponentMacros.ParseNetworkInputsExposedToScript(Component) %}
{% set ScriptableNetworkInputParamNames = ScriptableNetworkInputParamNames.append(LowerFirst(netInput.attrib['Name'])) %}
{% endcall %}
{% if NetworkInputsExposedToScriptCount > 0 %}
{{ ComponentName }}NetworkInput Construct{{ ComponentName }}NetworkInput({{ AutoComponentMacros.GetCommaSeparatedParamListOfScriptableNetworkInputs(Component) }})
{
return {{ ComponentName }}NetworkInput({{ ScriptableNetworkInputParamNames|join(', ') }});
}
{{ ComponentName }}NetworkInput::{{ ComponentName }}NetworkInput({{ AutoComponentMacros.GetCommaSeparatedParamListOfScriptableNetworkInputs(Component) }})
: {% for param_name in ScriptableNetworkInputParamNames %}m_{{ LowerFirst(param_name) }}({{ LowerFirst(param_name) }}){% if not loop.last %}, {% endif %}{% endfor -%}{}
void {{ ComponentName }}NetworkInput::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<{{ ComponentName }}NetworkInput>()
->Version(1)
;
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<{{ ComponentName }}NetworkInput>("{{ ComponentName }}NetworkInput")
->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}")
->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}")
{% set ScriptableNetInputNames = [] %}
{% call (netInput) AutoComponentMacros.ParseNetworkInputsExposedToScript(Component) %}
{% set ScriptableNetInputNames = ScriptableNetInputNames.append(netInput.attrib['Name']) %}
{% endcall %}
->Method("CreateFromValues", &Construct{{ ComponentName }}NetworkInput, { { {% for param_name in ScriptableNetInputNames %}{"{{ LowerFirst(param_name) }}"}{% if not loop.last %}, {% endif %}{% endfor -%} } })
{% for param_name in ScriptableNetInputNames %}
->Property("{{ param_name }}", BehaviorValueProperty(&{{ ComponentName }}NetworkInput::m_{{ LowerFirst(param_name) }}))
{% endfor %}
;
behaviorContext->EBus<{{ ComponentName }}RequestBus>("{{ ComponentName }}BusHandler")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}")
->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}")
->Handler<{{ ComponentName }}BusHandler>()
;
}
}
{% endif %}
Multiplayer::NetComponentId {{ ComponentName }}NetworkInput::GetNetComponentId() const
{
return {{ ComponentName }}NetworkInput::s_netComponentId;
@ -1347,6 +1398,26 @@ namespace {{ Component.attrib['Namespace'] }}
{% endif %}
}
{% if NetworkInputsExposedToScriptCount > 0 %}
void {{ ControllerBaseName }}::CreateInputFromScript([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
{
{{ ComponentName }}NetworkInput result;
{{ ComponentName }}RequestBus::EventResult(result, GetEntity()->GetId(), &{{ ComponentName }}RequestBus::Events::CreateInput, deltaTime);
// Inputs for your own component always exist
{{ ComponentName }}NetworkInput* {{ LowerFirst(ComponentName) }}Input = input.FindComponentInput<{{ ComponentName }}NetworkInput>();
{% call(netInput) AutoComponentMacros.ParseNetworkInputsExposedToScript(Component) %}
{{ LowerFirst(ComponentName) }}Input->m_{{ LowerFirst(netInput.attrib['Name']) }} = result.m_{{ LowerFirst(netInput.attrib['Name']) }};
{% endcall %}
}
void {{ ControllerBaseName }}::ProcessInputFromScript([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
{
{{ ComponentName }}NetworkInput* {{ LowerFirst(ComponentName) }}Input = input.FindComponentInput<{{ ComponentName }}NetworkInput>();
{{ ComponentName }}RequestBus::Event(GetEntity()->GetId(), &{{ ComponentName }}RequestBus::Events::ProcessInput, {{ LowerFirst(ComponentName) }}Input, deltaTime);
}
{% endif %}
const {{ ComponentName }}& {{ ControllerBaseName }}::GetParent() const
{
return static_cast<const {{ ComponentName }}&>(GetOwner());
@ -1399,6 +1470,9 @@ namespace {{ Component.attrib['Namespace'] }}
}
ReflectToEditContext(context);
ReflectToBehaviorContext(context);
{% if NetworkInputsExposedToScriptCount > 0 %}
{{ ComponentName }}NetworkInput::Reflect(context);
{% endif %}
}
void {{ ComponentBaseName }}::ReflectToEditContext(AZ::ReflectContext* context)
@ -1448,8 +1522,8 @@ namespace {{ Component.attrib['Namespace'] }}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Server', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Autonomous', 'Authority', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Autonomous', 'Authority', ComponentName) | indent(16) }}
// Reflect RPCs
{{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(4) -}}
{{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}}
@ -1459,7 +1533,6 @@ namespace {{ Component.attrib['Namespace'] }}
{{ 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) }}
;
}
@ -1508,7 +1581,6 @@ namespace {{ Component.attrib['Namespace'] }}
}
{{ ComponentBaseName }}::{{ ComponentBaseName }}() = default;
{{ ComponentBaseName }}::~{{ ComponentBaseName }}() = default;
void {{ ComponentBaseName }}::Init()

@ -278,6 +278,7 @@ namespace Multiplayer
AZ_Assert(IsNetEntityRoleAutonomous(), "Incorrect network role for input creation");
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
{
multiplayerComponent->GetController()->CreateInputFromScript(networkInput, deltaTime);
multiplayerComponent->GetController()->CreateInput(networkInput, deltaTime);
}
}
@ -289,6 +290,7 @@ namespace Multiplayer
AZ_Assert((NetworkRoleHasController(m_netEntityRole)), "Incorrect network role for input processing");
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
{
multiplayerComponent->GetController()->ProcessInputFromScript(networkInput, deltaTime);
multiplayerComponent->GetController()->ProcessInput(networkInput, deltaTime);
}
m_isProcessingInput = false;

Loading…
Cancel
Save