Merge remote-tracking branch 'origin' into MultiplayerComponents

This commit is contained in:
karlberg
2021-06-02 14:57:00 -07:00
640 changed files with 17532 additions and 12712 deletions
+38 -30
View File
@@ -58,9 +58,35 @@ ly_add_target(
Gem::CertificateManager
)
ly_add_target(
NAME Multiplayer.Debug ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
FILES_CMAKE
multiplayer_debug_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
.
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AtomCore
AZ::AzFramework
AZ::AzNetworking
Gem::Atom_Feature_Common.Static
Gem::Multiplayer.Static
Gem::ImGui.Static
)
# The "Multiplayer" target is used by clients and servers, Debug is used only on clients.
ly_create_alias(NAME Multiplayer.Clients NAMESPACE Gem TARGETS Gem::Multiplayer Gem::Multiplayer.Debug)
ly_create_alias(NAME Multiplayer.Servers NAMESPACE Gem TARGETS Gem::Multiplayer)
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME Multiplayer.Tools.Static STATIC
NAME Multiplayer.Builders.Static STATIC
NAMESPACE Gem
FILES_CMAKE
multiplayer_tools_files.cmake
@@ -80,10 +106,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
Gem::Multiplayer.Static
)
# by naming this target Multiplayer.Builders it ensures that it is loaded
# in any pipeline tools (Like Asset Processor, AssetBuilder, etc)
ly_add_target(
NAME Multiplayer.Tools MODULE
NAME Multiplayer.Builders GEM_MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.Multiplayer.Tools
OUTPUT_NAME Gem.Multiplayer.Builders
FILES_CMAKE
multiplayer_tools_files.cmake
INCLUDE_DIRECTORIES
@@ -94,7 +122,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
Include
BUILD_DEPENDENCIES
PRIVATE
Gem::Multiplayer.Tools.Static
Gem::Multiplayer.Builders.Static
)
ly_add_target(
@@ -121,9 +149,11 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AzNetworking
AZ::AzToolsFramework
Gem::Multiplayer.Static
Gem::Multiplayer.Tools
Gem::Multiplayer.Builders
)
# use the Multiplayer.Editor module in tools like the Editor: Such tools also get the visual debug view:
ly_create_alias(NAME Multiplayer.Tools NAMESPACE Gem TARGETS Gem::Multiplayer.Editor Gem::Multiplayer.Debug)
endif()
if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
@@ -151,7 +181,7 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME Multiplayer.Tools.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAME Multiplayer.Builders.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
multiplayer_tools_tests_files.cmake
@@ -165,33 +195,11 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
AZ::AzTest
AZ::AzTestShared
AZ::AzToolsFrameworkTestCommon
Gem::Multiplayer.Tools.Static
Gem::Multiplayer.Builders.Static
)
ly_add_googletest(
NAME Gem::Multiplayer.Tools.Tests
NAME Gem::Multiplayer.Builders.Tests
)
endif()
endif()
ly_add_target(
NAME Multiplayer.Debug ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
FILES_CMAKE
multiplayer_debug_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
.
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AtomCore
AZ::AzFramework
AZ::AzNetworking
Gem::Atom_Feature_Common.Static
Gem::Multiplayer.Static
Gem::ImGui.Static
)
@@ -16,6 +16,7 @@
#include <AzCore/Name/Name.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
@@ -41,7 +42,7 @@ namespace Multiplayer
//! This is a strong typedef for representing the number of application frames since application start.
AZ_TYPE_SAFE_INTEGRAL(HostFrameId, uint32_t);
static constexpr HostFrameId InvalidHostFrameId = HostFrameId{ 0xFFFFFFFF };
static constexpr HostFrameId InvalidHostFrameId = HostFrameId{ AzPhysics::SimulatedBody::UndefinedFrameId };
using LongNetworkString = AZ::CVarFixedString;
using ReliabilityType = AzNetworking::ReliabilityType;
@@ -0,0 +1,37 @@
/*
* 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
#include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
namespace Multiplayer
{
namespace Physics
{
//! Performs rewind-aware ray cast in the default physics world.
//! @param request The ray cast request to make.
//! @return Returns a structure that contains a list of Hits.
AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest& request);
//! Performs rewind-aware shape cast in the default physics world.
//! @param request The shape cast request to make.
//! @return Returns a structure that contains a list of Hits.
AzPhysics::SceneQueryHits ShapeCast(const AzPhysics::ShapeCastRequest& request);
//! Performs rewind-aware overlap in the default physics world.
//! @param request The overlap request to make.
//! @return Returns a structure that contains a list of Hits.
AzPhysics::SceneQueryHits Overlap(const AzPhysics::OverlapRequest& request);
} // namespace Physics
} // namespace Multiplayer
@@ -383,6 +383,8 @@ namespace {{ Component.attrib['Namespace'] }}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true)|indent(8) -}}
{{ DeclareArchetypePropertyGetters(Component)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}}
@@ -456,7 +458,6 @@ 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) -}}
@@ -481,8 +482,6 @@ namespace {{ Component.attrib['Namespace'] }}
//! @}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}}
{{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}}
{{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}}
@@ -118,22 +118,25 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index
bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value)
{
if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value))
int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size();
if (indexToSet < {{ Property.attrib['Count'] }})
{
int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size();
GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value);
int32_t bitIndex = indexToSet + static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }});
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true);
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().MarkDirty();
return true;
}
return false;
}
bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack(const Multiplayer::NetworkInput&)
{
if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back())
if (!GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty())
{
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back();
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().MarkDirty();
return true;
}
@@ -216,10 +219,11 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index
bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value)
{
if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value))
int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size();
if (indexToSet < {{ Property.attrib['Count'] }})
{
uint32_t indexToSet = aznumeric_cast<uint32_t>(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size());
uint32_t bitIndex = indexToSet + aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }});
GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value);
int32_t bitIndex = indexToSet + static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }});
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true);
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().MarkDirty();
@@ -230,9 +234,10 @@ bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{
bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack()
{
if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back())
if (!GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty())
{
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back();
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().MarkDirty();
return true;
}
@@ -241,7 +246,7 @@ bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack()
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear()
{
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true);
GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.clear();
GetParent().MarkDirty();
}
@@ -586,8 +591,16 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
const uint32_t lastBit = static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'End') }});
{% endif %}
{% if Property.attrib['IsRewindable']|booleanTrue %}
AzNetworking::FixedSizeBitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1);
m_{{ LowerFirst(Property.attrib['Name']) }}.Serialize(serializer, deltaRecord);
{% else %}
{% if Property.attrib['Container'] == 'Vector' %}
serializer.Serialize<AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}");
{% elif Property.attrib['Container'] == 'Array' %}
serializer.Serialize<AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}");
{% endif %}
{% endif %}
}
{% else %}
Multiplayer::SerializeNetworkPropertyHelper
@@ -618,11 +631,11 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam
{% if (Property.attrib['GenerateEventBindings']|booleanTrue) %}
{% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %}
// NotifyChangesAuthorityToClientProperties for Arrays and Vectors
for (uint32_t bitIndex = static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}), elementIndex = 0; bitIndex <= static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component, ReplicateFrom, ReplicateTo, Property, 'End') }}); ++bitIndex, ++elementIndex)
for (uint32_t bitIndex = static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}), elementIndex = 0; bitIndex <= static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'End') }}); ++bitIndex, ++elementIndex)
{
if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(bitIndex){% if Property.attrib['Container'] == 'Vector' %} && elementIndex < m_{{ Property.attrib['Name'] }}.GetSize(){% endif %})
if (replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.GetBit(bitIndex){% if Property.attrib['Container'] == 'Vector' %} && elementIndex < m_{{ LowerFirst(Property.attrib['Name']) }}.size(){% endif %})
{
m_LowerFirst( Property.attrib['Name']) }}Event.Signal(elementIndex, m_{{ LowerFirst(Property.attrib['Name']) }}[elementIndex]);
m_{{ LowerFirst(Property.attrib['Name']) }}Event.Signal(elementIndex, m_{{ LowerFirst(Property.attrib['Name']) }}[elementIndex]);
}
}
{% if Property.attrib['Container'] == 'Vector' %}
@@ -783,12 +796,27 @@ enum class NetworkProperties
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} 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 {{ Property.attrib['Type'] }}();
}
{% if (ReplicateTo == 'Autonomous') or (ReplicateFrom == 'Autonomous' and ReplicateTo == 'Authority') %}
// {{ UpperFirst(Property.attrib['Name']) }} is only sent and received between contoller objects (ie Authority, Autonomous); we must go through the controller in order to get this property
{{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController());
if (!controller)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This property is replicated to autonomous network entities, because this entity doesn't have a controller, it must not be automonous. Please check your network context before attempting to get {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str())
return {{ Property.attrib['Type'] }}();
}
{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %}
return controller->Get{{ UpperFirst(Property.attrib['Name']) }}(index);
{% else %}
return controller->Get{{ UpperFirst(Property.attrib['Name']) }}();
{% endif %}
{% else %}
{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %}
return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}(index);
{% else %}
return networkComponent->Get{{ UpperFirst(Property.attrib['Name']) }}();
{% endif %}
{% endif %}
})
{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %}
->Method("Set{{ UpperFirst(Property.attrib['Name']) }}", [](AZ::EntityId id, int32_t index, const {{ Property.attrib['Type'] }}& value) -> void
@@ -1467,12 +1495,8 @@ namespace {{ Component.attrib['Namespace'] }}
}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}}
{{ 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) -}}
{{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}}
@@ -469,19 +469,16 @@ namespace Multiplayer
m_connAcquiredEvent.Signal(datum);
}
// Hosts will spawn a new default player prefab for the user that just connected
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()), 1);
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity());
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
if (controlledEntity.Exists())
{
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
{
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
@@ -553,6 +550,17 @@ namespace Multiplayer
}
}
m_agentType = multiplayerType;
// Spawn the default player for this host since the host is also a player (not a dedicated server)
if (m_agentType == MultiplayerAgentType::ClientServer)
{
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
{
controlledEntityNetBindComponent->SetAllowAutonomy(true);
}
}
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
}
@@ -728,6 +736,19 @@ namespace Multiplayer
}
}
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab()
{
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()), 1);
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity());
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
}
return controlledEntity;
}
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer;
@@ -105,7 +105,8 @@ namespace Multiplayer
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
NetworkEntityHandle SpawnDefaultPlayerPrefab();
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
AzNetworking::INetworkInterface* m_networkInterface = nullptr;
@@ -402,8 +402,12 @@ namespace Multiplayer
const AZ::Transform& transform
)
{
INetworkEntityManager::EntityList returnList;
EntityList returnList;
if (!AZ::Data::AssetManager::IsReady())
{
return returnList;
}
auto spawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(prefabEntryId.m_prefabName);
// Required for sync-instantiation. Todo: keep the reference in NetworkSpawnableLibrary
auto netSpawnableAsset = AZ::Data::AssetManager::Instance().GetAsset<AzFramework::Spawnable>(spawnableAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
@@ -0,0 +1,104 @@
/*
* 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.
*
*/
#include <Include/Multiplayer/Physics/PhysicsUtils.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/Physics/PhysicsScene.h>
#include <AzFramework/Physics/Shape.h>
#include <Multiplayer/NetworkTime/INetworkTime.h>
namespace
{
template<typename RequestT>
AzPhysics::SceneQueryHits SceneQueryInternal(const RequestT& request)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
if (!sceneInterface)
{
return {};
}
AzPhysics::SceneHandle sceneHandle = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
if (sceneHandle == AzPhysics::InvalidSceneHandle)
{
return {};
}
Multiplayer::INetworkTime* currentNetTime = Multiplayer::GetNetworkTime();
if(!currentNetTime->IsTimeRewound())
{
// If the time is not rewound, we simply execute the scene query as is.
AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &request);
return result;
}
// If the time is rewound, we want to query against rigid bodies present at the same frame ID: the same as the current rewound time is.
RequestT netSceneQueryRequest = request;
netSceneQueryRequest.m_filterCallback = [&request, currentFrameId = (uint32_t)currentNetTime->GetHostFrameId()](
const AzPhysics::SimulatedBody* body, const ::Physics::Shape* shape)
{
if (body->GetFrameId() == AzPhysics::SimulatedBody::UndefinedFrameId || body->GetFrameId() == currentFrameId)
{
if (request.m_filterCallback)
{
return request.m_filterCallback(body, shape);
}
// Overlap filter callbacks return true/false rather than Touch/Block/None
if constexpr (AZStd::is_same_v<RequestT, AzPhysics::OverlapRequest>)
{
return true;
}
else
{
return AzPhysics::SceneQuery::QueryHitType::Touch;
}
}
if constexpr (AZStd::is_same_v<RequestT, AzPhysics::OverlapRequest>)
{
return false;
}
else
{
return AzPhysics::SceneQuery::QueryHitType::None;
}
};
// Execute the scene query modified for the time rewind.
AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &netSceneQueryRequest);
return result;
}
}
namespace Multiplayer
{
namespace Physics
{
AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest& request)
{
return SceneQueryInternal<AzPhysics::RayCastRequest>(request);
}
AzPhysics::SceneQueryHits ShapeCast(const AzPhysics::ShapeCastRequest& request)
{
return SceneQueryInternal<AzPhysics::ShapeCastRequest>(request);
}
AzPhysics::SceneQueryHits Overlap(const AzPhysics::OverlapRequest& request)
{
return SceneQueryInternal<AzPhysics::OverlapRequest>(request);
}
} // namespace Physics
} // namespace Multiplayer
@@ -59,7 +59,7 @@ namespace Multiplayer
AZ::Transform worldTm = GetEntity()->FindComponent<AzFramework::TransformComponent>()->GetWorldTM();
auto preInsertionCallback =
[worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()]
(AzFramework::EntitySpawnTicket&, AzFramework::SpawnableEntityContainerView entities)
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
{
if (entities.size() == 1)
{
@@ -81,7 +81,8 @@ namespace Multiplayer
};
m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset);
AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities(m_netSpawnTicket, {m_netEntityIndex}, preInsertionCallback);
AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities(
m_netSpawnTicket, AzFramework::SpawnablePriority_Default, { m_netEntityIndex }, preInsertionCallback);
}
}
@@ -89,7 +90,7 @@ namespace Multiplayer
{
if(m_netSpawnTicket.IsValid())
{
AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket);
AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket, AzFramework::SpawnablePriority_Default);
}
}
@@ -39,6 +39,7 @@ set(FILES
Include/Multiplayer/NetworkTime/RewindableFixedVector.inl
Include/Multiplayer/NetworkTime/RewindableObject.h
Include/Multiplayer/NetworkTime/RewindableObject.inl
Include/Multiplayer/Physics/PhysicsUtils.h
Include/Multiplayer/ReplicationWindows/IReplicationWindow.h
Source/Multiplayer_precompiled.cpp
Source/Multiplayer_precompiled.h
@@ -107,6 +108,7 @@ set(FILES
Source/Pipeline/NetBindMarkerComponent.h
Source/Pipeline/NetworkSpawnableHolderComponent.cpp
Source/Pipeline/NetworkSpawnableHolderComponent.h
Source/Physics/PhysicsUtils.cpp
Source/ReplicationWindows/NullReplicationWindow.cpp
Source/ReplicationWindows/NullReplicationWindow.h
Source/ReplicationWindows/ServerToClientReplicationWindow.cpp