/* * 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 "StdAfx.h" #include "IGameRulesSystem.h" #include "ILevelSystem.h" #include "NetworkGridMate.h" #include "NetworkGridmateDebug.h" #include "Replicas/EntityReplica.h" #include "Replicas/EntityScriptReplicaChunk.h" #include "Replicas/GameContextReplica.h" #include "Compatibility/GridMateNetSerialize.h" #include "Compatibility/GridMateRMI.h" #include <GridMate/Replica/ReplicaFunctions.h> #include <GridMate/Replica/BasicHostChunkDescriptor.h> #include "Components/IComponentUser.h" #include "NetworkGridMateEntityEventBus.h" #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define NETWORKGRIDMATE_CPP_SECTION_1 1 #define NETWORKGRIDMATE_CPP_SECTION_2 2 #endif #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION NETWORKGRIDMATE_CPP_SECTION_1 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/NetworkGridMate_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/NetworkGridMate_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/NetworkGridMate_cpp_salem.inl" #endif #endif namespace GridMate { //----------------------------------------------------------------------------- Network* Network::s_instance = nullptr; int Network::s_StatsIntervalMS = 1000; // 1 second by default. int Network::s_DumpStatsEnabled = 0; FILE* Network::s_DumpStatsFile = nullptr; //----------------------------------------------------------------------------- Network::Network() : m_localChannelId(kOfflineChannelId) , m_gameContext(nullptr) , m_gridMate(nullptr) , m_session(nullptr) , m_gameContextReplica(nullptr) , m_levelLoadState(LevelLoadState_None) , m_allowMinimalUpdate(false) { s_instance = this; m_legacySerializeProvider = this; m_postFrameTasks.reserve(32); } //----------------------------------------------------------------------------- Network::~Network() { #if GRIDMATE_DEBUG_ENABLED Debug::UnregisterCVars(); #endif if (GetLevelSystem()) { GetLevelSystem()->RemoveListener(this); } if (gEnv && gEnv->pEntitySystem) { gEnv->pEntitySystem->RemoveSink(this); } m_activeEntityReplicaMap.clear(); m_newProxyEntities.clear(); ShutdownGridMate(); if (s_DumpStatsFile) { fclose(s_DumpStatsFile); s_DumpStatsFile = nullptr; } s_instance = nullptr; } //----------------------------------------------------------------------------- bool Network::Init(int ncpu) { // Register for entity system callbacks. GM_ASSERT_TRACE(gEnv && gEnv->pEntitySystem, "Entity system should already be initialized."); if (gEnv && gEnv->pEntitySystem) { gEnv->pEntitySystem->AddSink(this, IEntitySystem::OnSpawn | IEntitySystem::OnRemove, 0); } #if GRIDMATE_DEBUG_ENABLED Debug::RegisterCVars(); #endif StartGridMate(); MarkAsLocalOnly(); return true; } //----------------------------------------------------------------------------- Network& Network::Get() { GM_ASSERT_TRACE(s_instance, "Network interface has not yet been created."); return *s_instance; } //----------------------------------------------------------------------------- void Network::Release() { delete this; } //----------------------------------------------------------------------------- void Network::SetGameContext(IGameContext* gameContext) { if (GetLevelSystem()) { // try to add ourselves (in case we are not already) GetLevelSystem()->AddListener(this); } m_gameContext = gameContext; if (m_gameContextReplica) { m_gameContextReplica->BindGameContext(gameContext); } } //----------------------------------------------------------------------------- bool Network::AllowEntityCreation() const { // Disallow entity creation during level transitions. if (m_gameContextReplica) { return m_levelLoadState == LevelLoadState_Loaded; } return false; } //----------------------------------------------------------------------------- void Network::SetGameContextReplica(GameContextReplica* contextReplica) { m_gameContextReplica = contextReplica; if (contextReplica) { contextReplica->BindGameContext(m_gameContext); } } //----------------------------------------------------------------------------- bool Network::IsInMinimalUpdate() const { return m_allowMinimalUpdate; } //----------------------------------------------------------------------------- void Network::SyncWithGame(ENetworkGameSync syncType) { FUNCTION_PROFILER(GetISystem(), PROFILE_NETWORK); switch (syncType) { case eNGS_FrameStart: { UpdateGridMate(syncType); } break; case eNGS_FrameEnd: { FlushPostFrameTasks(); UpdateGridMate(syncType); if (m_gameContextReplica) { FRAME_PROFILER("GameContextReplica Update", GetISystem(), PROFILE_NETWORK); m_gameContextReplica->SyncWithGame(); } UpdateNetworkStatistics(); DebugDraw(); } break; ///////////////////////////////////////////////////////////////////////////////////////////// // Inherited from CryNetwork, this mechanism is required for safe updating during loading. // During such time, the network is pumped via the NetworkStallerTicker thread, and this flag // basically describes when it's safe for network messages to be distributed to the game. case eNGS_AllowMinimalUpdate: { m_allowMinimalUpdate = true; m_levelLoadState = LevelLoadState_Loading; } break; case eNGS_DenyMinimalUpdate: { m_allowMinimalUpdate = false; m_levelLoadState = LevelLoadState_Loaded; } break; case eNGS_MinimalUpdateForLoading: { if (m_allowMinimalUpdate) { UpdateGridMate(syncType); } } break; ///////////////////////////////////////////////////////////////////////////////////////////// } } //----------------------------------------------------------------------------- void Network::FlushPostFrameTasks() { BindNewEntitiesToNetwork(); for (const Task& task : m_postFrameTasks) { task(); } RMI::FlushQueue(); m_postFrameTasks.clear(); } //----------------------------------------------------------------------------- void Network::UpdateGridMate(ENetworkGameSync syncType) { if (m_gridMate && m_mutexUpdatingGridMate.try_lock() ) { FRAME_PROFILER("GridMate Update", GetISystem(), PROFILE_NETWORK); GridMate::ReplicaManager* replicaManager = GetCurrentSession() ? GetCurrentSession()->GetReplicaMgr() : nullptr; if (replicaManager) { switch (syncType) { case eNGS_MinimalUpdateForLoading: case eNGS_FrameStart: { if (replicaManager) { replicaManager->Unmarshal(); replicaManager->UpdateFromReplicas(); } // When called from the network stall ticker thread, marshalling should be performed as well. if (syncType != eNGS_MinimalUpdateForLoading) { break; } } case eNGS_FrameEnd: { if (replicaManager) { replicaManager->UpdateReplicas(); replicaManager->Marshal(); } break; } default: break; } } m_gridMate->Update(); m_mutexUpdatingGridMate.unlock(); } } //----------------------------------------------------------------------------- ChannelId Network::GetChannelIdForSessionMember(GridMate::GridMember* member) const { return member ? ChannelId(member->GetIdCompact()) : kInvalidChannelId; } //----------------------------------------------------------------------------- void Network::ChangedAspects(EntityId entityId, NetworkAspectType aspectBits) { if (aspectBits == 0) { return; // nothing to do } #ifndef _RELEASE for (size_t i = NetSerialize::kNumAspectSlots; i < NUM_ASPECTS; ++i) { if (BIT64(i) & aspectBits) { GM_ASSERT_TRACE(0, "Any aspects >= %u can not be serialized through this layer, until support for > 32 data sets is enabled.", static_cast<uint32>(NetSerialize::kNumAspectSlots)); break; } } #endif EntityReplica* replica = FindEntityReplica(entityId); if (replica) { if (replica->IsMaster() || replica->IsAspectDelegatedToThisClient()) { NetworkAspectType oldDirtyAspects = replica->GetDirtyAspects(); replica->MarkAspectsDirty(aspectBits); if (replica->IsAspectDelegatedToThisClient()) { // Only add the task if these are the first aspects being dirtied. if (oldDirtyAspects == 0) { m_postFrameTasks.push_back( [=]() { EntityReplica* rep = FindEntityReplica(entityId); if (rep) { rep->UploadClientDelegatedAspects(); } } ); } } } } else { GM_DEBUG_TRACE("Failed to mark aspects dirty because replica for " "entity id %u could not be found.", entityId); } } //----------------------------------------------------------------------------- ChannelId Network::GetLocalChannelId() const { return m_localChannelId; } //----------------------------------------------------------------------------- ChannelId Network::GetServerChannelId() const { if (m_session) { return GetChannelIdForSessionMember(m_session->GetHost()); } return m_localChannelId; } //----------------------------------------------------------------------------- EntityId Network::LocalEntityIdToServerEntityId(EntityId localId) const { if (!gEnv->bServer) { // \todo - Optimize. Keep a local->server id map locally. We already have server->local // via m_activeEntityReplicaMap. for (auto& replicaEntry : m_activeEntityReplicaMap) { if (replicaEntry.second->GetLocalEntityId() == localId) { return replicaEntry.first; } } return kInvalidEntityId; } return localId; } //----------------------------------------------------------------------------- EntityId Network::ServerEntityIdToLocalEntityId(EntityId serverId, bool allowForcedEstablishment /*= false*/) const { EntityId localId = kInvalidEntityId; if (gEnv->bServer) { localId = serverId; } else { auto foundAt = m_activeEntityReplicaMap.find(serverId); if (foundAt != m_activeEntityReplicaMap.end()) { EntityReplicaPtr replica = foundAt->second; localId = replica->GetLocalEntityId(); } else if (allowForcedEstablishment) { AZ_Assert(AllowEntityCreation(), "Entity creation is not allowed during level loads! Forcing creation is going to cause problems!"); // If we're deserializing this entity Id via the 'eid' policy, but the local entity is not // yet established, expedite establishment. This is to ensure we can properly map/decode // the entity Id mid-serialization. auto newProxy = m_newProxyEntities.find(serverId); if (newProxy != m_newProxyEntities.end()) { EntityReplicaPtr replica = newProxy->second; localId = replica->HandleNewlyReceivedNow(); } } } return localId; } //----------------------------------------------------------------------------- void Network::InvokeRMI(IGameObject* gameObject, IRMIRep& rep, void* params, ChannelId targetChannelFilter, uint32 where) { RMI::InvokeLegacy(gameObject, rep, params, targetChannelFilter, where); } //----------------------------------------------------------------------------- void Network::InvokeActorRMI(EntityId entityId, uint8 actorExtensionId, ChannelId targetChannelFilter, IActorRMIRep& rep) { RMI::InvokeActor(entityId, actorExtensionId, targetChannelFilter, rep); } //----------------------------------------------------------------------------- void Network::InvokeScriptRMI(ISerializable* serializable, bool isServerRMI, ChannelId toChannelId, ChannelId avoidChannelId) { RMI::InvokeScript(serializable, isServerRMI, toChannelId, avoidChannelId); } //----------------------------------------------------------------------------- void Network::RegisterActorRMI(IActorRMIRep* rep) { RMI::RegisterActorRMI(rep); } //----------------------------------------------------------------------------- void Network::UnregisterActorRMI(IActorRMIRep* rep) { RMI::UnregisterActorRMI(rep); } //----------------------------------------------------------------------------- void Network::SetDelegatableAspectMask(NetworkAspectType aspectBits) { NetSerialize::SetDelegatableAspects(aspectBits); } //----------------------------------------------------------------------------- void Network::SetObjectDelegatedAspectMask(EntityId entityId, NetworkAspectType aspects, bool set) { m_postFrameTasks.push_back( [=]() { if (EntityReplica* entityReplica = FindEntityReplica(entityId)) { NetworkAspectType mask = entityReplica->GetClientDelegatedAspectMask(); if (set) { mask |= aspects; } else { mask &= ~aspects; } entityReplica->SetClientDelegatedAspectMask(mask); } else { GM_DEBUG_TRACE("Failed to update aspect delegation mask because replica" "for entity id %u could not be found.", entityId); } } ); } //----------------------------------------------------------------------------- void Network::DelegateAuthorityToClient(EntityId entityId, ChannelId clientChannelId) { GridMate::EntityReplica* replica = FindEntityReplica(entityId); if (replica) { replica->RPCDelegateAuthorityToOwner(clientChannelId); } } void Network::ShutdownGridMate() { if (m_gridMate) { GM_DEBUG_TRACE("Shutting down GridMate network."); m_postFrameTasks.clear(); RMI::EmptyQueue(); GridMateDestroy(m_gridMate); m_gridMate = nullptr; if (m_sessionEvents.IsConnected()) { m_sessionEvents.Disconnect(); } if (m_systemEvents.IsConnected()) { m_systemEvents.Disconnect(); } // TEMP: Moved GridMateAllocatorMP creation into AzFramework::Application due to a shutdown issue // with allocator environment variables. AzFramework::Application will create GridMateAllocatorMP from // the main process. // See LMBR-20396 for more details. //AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy(); } } //----------------------------------------------------------------------------- EntityReplica* Network::FindEntityReplica(EntityId id) const { if (!gEnv->bServer) { // Replicas are mapped by server-side entity Id, and we map back and forth // to reconcile across server and clients. // Upon deserializing via 'eid' policy, server-side Ids are converted back // to local. id = LocalEntityIdToServerEntityId(id); } auto replicaIter = m_activeEntityReplicaMap.find(id); if (replicaIter != m_activeEntityReplicaMap.end()) { return replicaIter->second.get(); } return nullptr; } //----------------------------------------------------------------------------- void Network::StartGridMate() { if (nullptr != m_gridMate) { return; } GridMateDesc desc; m_gridMate = GridMateCreate(desc); // Initializing GridMate multiplayer service allocator // TEMP: Moved GridMateAllocatorMP creation into AzFramework::Application due to a shutdown issue // with allocator environment variables. AzFramework::Application will create GridMateAllocatorMP from // the main process. // See LMBR-20396 for more details. //GridMate::GridMateAllocatorMP::Descriptor allocDesc; //allocDesc.m_custom = &AZ::AllocatorInstance<AZ::SystemAllocator>::Get(); //AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create(allocDesc); // Monitor session events. GM_ASSERT_TRACE(!m_sessionEvents.IsConnected(), "Session events bus should not be connected yet."); m_sessionEvents.Connect(m_gridMate); // Monitor internal system events. GM_ASSERT_TRACE(!m_systemEvents.IsConnected(), "System events bus should not be connected yet."); m_systemEvents.Connect(); if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(EntityReplica::GetChunkName()))) { ReplicaChunkDescriptorTable::Get().RegisterChunkType<EntityReplica, EntityReplica::EntityReplicaDesc>(); } if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(EntityScriptReplicaChunk::GetChunkName()))) { ReplicaChunkDescriptorTable::Get().RegisterChunkType<EntityScriptReplicaChunk>(); } if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(GameContextReplica::GetChunkName()))) { ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameContextReplica, GridMate::BasicHostChunkDescriptor<GameContextReplica>>(); } #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION NETWORKGRIDMATE_CPP_SECTION_2 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/NetworkGridMate_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/NetworkGridMate_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/NetworkGridMate_cpp_salem.inl" #endif #endif } //----------------------------------------------------------------------------- void Network::OnLoadingComplete(ILevel* level) { if (level != nullptr) { if (m_gameContextReplica) { // Notify the server we've successfully loaded the map, so our representative // actor can be created. m_gameContextReplica->RPCOnMemberLoadedMap(); } else if (gEnv->IsClient() || gEnv->IsEditor()) { // This is the single player case. Trigger local actor spawn. if (IGameRules* gameRules = GetGameRules()) { gameRules->OnClientConnect(m_localChannelId, false); // Can become part of GameContext once GameContext is directly bound to its replica (so it can tell if it is in MP or SP). gameRules->OnClientEnteredGame(m_localChannelId, false); // Should be triggered by actor creation on the client, and forwarded by the local GameContext to the server } } } } //----------------------------------------------------------------------------- void Network::OnUnloadComplete(ILevel* level) { m_levelLoadState = LevelLoadState_None; m_activeEntityReplicaMap.clear(); m_newServerEntities.clear(); } //----------------------------------------------------------------------------- CTimeValue Network::GetSessionTime() { CTimeValue t = gEnv->pTimer->GetFrameStartTime(); if (m_session) { t.SetMilliSeconds(m_session->GetTime()); } return t; } //----------------------------------------------------------------------------- void Network::UpdateNetworkStatistics() { static float s_lastUpdate = 0.f; const float time = gEnv->pTimer->GetCurrTime(ITimer::ETIMER_UI); if (time >= s_lastUpdate + (s_StatsIntervalMS * 0.001f)) { FUNCTION_PROFILER(GetISystem(), PROFILE_NETWORK); s_lastUpdate = time; if (m_session) { Carrier* carrier = m_session->GetCarrier(); for (unsigned int i = 0; i < m_session->GetNumberOfMembers(); ++i) { GridMember* member = m_session->GetMemberByIndex(i); if (member == m_session->GetMyMember()) { continue; } const ConnectionID connId = member->GetConnectionId(); if (connId != InvalidConnectionID) { TrafficControl::Statistics stats; carrier->QueryStatistics(connId, &stats); CarrierStatistics& memberStats = m_statisticsPerChannel[ GetChannelIdForSessionMember(member) ]; memberStats.m_rtt = stats.m_rtt; memberStats.m_packetLossRate = stats.m_packetLoss; memberStats.m_totalReceivedBytes = stats.m_dataReceived; memberStats.m_totalSentBytes = stats.m_dataSend; memberStats.m_packetsLost = stats.m_packetLost; memberStats.m_packetsReceived = stats.m_packetReceived; memberStats.m_packetsSent = stats.m_packetSend; } } } #if GRIDMATE_DEBUG_ENABLED if (s_DumpStatsEnabled > 0) { DumpNetworkStatistics(); m_gameStatistics = GameStatistics(); } #endif // GRIDMATE_DEBUG_ENABLED } } //----------------------------------------------------------------------------- void Network::ClearNetworkStatistics() { m_gameStatistics = GameStatistics(); m_statisticsPerChannel.clear(); } //----------------------------------------------------------------------------- GameStatistics& Network::GetGameStatistics() { return m_gameStatistics; } //----------------------------------------------------------------------------- CarrierStatistics Network::GetCarrierStatistics() { if (!m_statisticsPerChannel.empty()) { return m_statisticsPerChannel.begin()->second; } return CarrierStatistics(); } //----------------------------------------------------------------------------- // IEntitySystemSink callbacks. //----------------------------------------------------------------------------- void Network::OnSpawn(IEntity* pEntity, SEntitySpawnParams& params) { // Every time the game/engine spawns an entity, this is where we hear about it. // This is essentially where we handle "binding to the network." // If it's a networked entity, we simply spawn a replica for it. GM_DEBUG_TRACE("Entity Spawned: \"%s\" [%u]", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); if (gEnv->bServer) { // Ignore the entity if it doesn't need to be bound. if (!!(pEntity->GetFlags() & (ENTITY_FLAG_CLIENT_ONLY | ENTITY_FLAG_SERVER_ONLY))) { GM_DEBUG_TRACE("Skipping replica creation for entity \"%s\" [%d] because it's not marked for networking.", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); return; } const EntityId id = pEntity->GetId(); // Pull the channel Id from the actor, if this is indeed an actor. We need to serialize // the owning channel Id, so when clients replicate the entity on their end, they know // if it's theirs or another remote client's representative actor. IActorSystem* actorSystem = GetActorSystem(); IGameRulesSystem* gameRulesSystem = GetGameRulesSystem(); IActor* actor = actorSystem ? actorSystem->GetActor(id) : nullptr; const ChannelId channelId = actor ? actor->GetChannelId() : kInvalidChannelId; if (channelId != kInvalidChannelId) { GM_DEBUG_TRACE("Entity %s is bound to channel Id %u", pEntity->GetName(), channelId); } uint32 paramsFlags = EntitySpawnParamsStorage::kParamsFlag_None; if (gameRulesSystem && gameRulesSystem->GetCurrentGameRulesEntity() == pEntity) { paramsFlags |= EntitySpawnParamsStorage::kParamsFlag_IsGameRules; } else if (m_levelLoadState == LevelLoadState_Loading && channelId == kInvalidChannelId) { paramsFlags |= EntitySpawnParamsStorage::kParamsFlag_IsLevelEntity; } // Undocumented behavior maintained from old network spawn logic. // Evidently clients should not inherit this flag from the server. params.nFlags = (params.nFlags & ~ENTITY_FLAG_TRIGGER_AREAS); EntitySpawnParamsStorage paramsStorage(params, channelId, paramsFlags); if (m_session && m_session->IsHost()) { GM_DEBUG_TRACE("Replica created for entity \"%s\" [%u]", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); m_newServerEntities[ paramsStorage.m_id ] = paramsStorage; } else if (!gEnv->IsEditor()) { GM_DEBUG_TRACE("Replica creation skipped for entity \"%s\" [%u] because we're not in a session", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); } } } //----------------------------------------------------------------------------- bool Network::OnRemove(IEntity* pEntity) { GM_DEBUG_TRACE("Entity Removed: \"%s\" [%u]", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); const EntityId id = pEntity->GetId(); // Ensure any deferred adds are canceled. m_newServerEntities.erase(id); for (auto iter = m_activeEntityReplicaMap.begin(); iter != m_activeEntityReplicaMap.end(); ++iter) { EntityReplicaPtr entityChunk = iter->second; if (entityChunk->GetLocalEntityId() == pEntity->GetId()) { // Destroy the replica if we own it. if (entityChunk->IsMaster()) { GM_DEBUG_TRACE("Destroyed owned replica for removed entity \"%s\" [%u]", pEntity ? pEntity->GetName() : "<null>", pEntity ? pEntity->GetId() : 0); entityChunk->GetReplica()->Destroy(); } break; } } // Don't disable removal; we just wanted to intercept it. return true; } //----------------------------------------------------------------------------- void Network::BindNewEntitiesToNetwork() { if (m_session && m_session->IsHost()) { // Create replicas for new spawned during level load. // Only create replica if the entity is still relevant. for (auto& newEntity : m_newServerEntities) { if (gEnv->pEntitySystem->GetEntity(newEntity.first)) { ReplicaPtr replica = Replica::CreateReplica(newEntity.second.m_entityName.c_str()); EntityReplica* entityChunk = CreateReplicaChunk<EntityReplica>(newEntity.second); replica->AttachReplicaChunk(entityChunk); EntityScriptReplicaChunk* scriptEntityChunk = CreateReplicaChunk<EntityScriptReplicaChunk>(); replica->AttachReplicaChunk(scriptEntityChunk); EBUS_EVENT_ID(newEntity.first, NetworkGridMateEntityEventBus, OnEntityBoundToNetwork, replica); m_session->GetReplicaMgr()->AddMaster(replica); } } } m_newServerEntities.clear(); for (EntityReplicaMap::iterator iterNewProxy = m_newProxyEntities.begin(); iterNewProxy != m_newProxyEntities.end(); ) { EntityReplicaPtr entityChunk = iterNewProxy->second; entityChunk->HandleNewlyReceivedNow(); if ((entityChunk->GetFlags() & EntityReplica::kFlag_NewlyReceived) == 0) { iterNewProxy = m_newProxyEntities.erase(iterNewProxy); } else { ++iterNewProxy; } } } //----------------------------------------------------------------------------- void Network::GetBandwidthStatistics(SBandwidthStats* const pStats) { pStats->m_numChannels = m_statisticsPerChannel.size(); if (!m_statisticsPerChannel.empty()) { const auto& carrierStats = m_statisticsPerChannel.begin()->second; pStats->m_1secAvg.m_totalPacketsDropped = carrierStats.m_packetsLost; pStats->m_1secAvg.m_totalPacketsRecvd = carrierStats.m_packetsReceived; pStats->m_1secAvg.m_totalPacketsSent = carrierStats.m_packetsSent; pStats->m_1secAvg.m_totalBandwidthRecvd = carrierStats.m_totalReceivedBytes; pStats->m_1secAvg.m_totalBandwidthSent = carrierStats.m_totalSentBytes; } } //----------------------------------------------------------------------------- void Network::GetPerformanceStatistics(SNetworkPerformance* pSizer) { // Network Cpu stats. } //----------------------------------------------------------------------------- void Network::GetProfilingStatistics(SNetworkProfilingStats* const pStats) { pStats->m_maxBoundObjects = uint(~0); pStats->m_numBoundObjects = m_activeEntityReplicaMap.size(); // pStats->m_ProfileInfoStats Part of NET_PROFILE macros } //! Called when need serializer for the legacy aspect void Network::AcquireSerializer(WriteBuffer& wb, NetSerialize::AcquireSerializeCallback callback) { NetSerialize::EntityNetSerializerCollectState serializerImpl(wb); CSimpleSerialize<NetSerialize::EntityNetSerializerCollectState> serializer(serializerImpl); callback(&serializer); } //! Called when need deserializer for the legacy aspect void Network::AcquireDeserializer(ReadBuffer& rb, NetSerialize::AcquireSerializeCallback callback) { NetSerialize::EntityNetSerializerDispatchState serializerImpl(rb); CSimpleSerialize<NetSerialize::EntityNetSerializerDispatchState> serializer(serializerImpl); callback(&serializer); } //----------------------------------------------------------------------------- void Network::MarkAsConnectedServer() { CryLog("Marked as hosting server."); gEnv->bServer = true; gEnv->bMultiplayer = true; if (m_gameContext) { uint32 flags = m_gameContext->GetContextFlags(); flags &= ~(eGSF_LocalOnly); flags |= (eGSF_Server); m_gameContext->SetContextFlags(flags); } } //----------------------------------------------------------------------------- void Network::MarkAsConnectedClient() { CryLog("Marked as connected client."); gEnv->bServer = false; gEnv->bMultiplayer = true; if (m_gameContext) { uint32 flags = m_gameContext->GetContextFlags(); flags &= ~(eGSF_Server | eGSF_LocalOnly); m_gameContext->SetContextFlags(flags); } } //----------------------------------------------------------------------------- void Network::MarkAsLocalOnly() { CryLog("Marked as local only."); gEnv->bServer = true; gEnv->bMultiplayer = false; if (m_gameContext) { uint32 flags = m_gameContext->GetContextFlags(); flags |= (eGSF_Server | eGSF_LocalOnly); m_gameContext->SetContextFlags(flags); } } } // namespace GridMate