using System;
using System.Collections.Generic;
using Prometheus;
using Robust.Client.GameStates;
using Robust.Client.Player;
using Robust.Client.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Replays;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
///
/// Manager for entities -- controls things like template loading and instantiation
///
public sealed partial class ClientEntityManager : EntityManager, IClientEntityManagerInternal
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientNetManager _networkManager = default!;
[Dependency] private readonly IClientGameTiming _gameTiming = default!;
[Dependency] private readonly IClientGameStateManager _stateMan = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
public override void Initialize()
{
SetupNetworking();
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
base.Initialize();
}
public override void FlushEntities()
{
// Server doesn't network deletions on client shutdown so we need to
// manually clear these out or risk stale data getting used.
PendingNetEntityStates.Clear();
using var _ = _gameTiming.StartStateApplicationArea();
base.FlushEntities();
}
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, out MetaDataComponent metadata)
{
return base.CreateEntity(prototypeName, out metadata);
}
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
{
base.InitializeEntity(entity, meta);
}
void IClientEntityManagerInternal.StartEntity(EntityUid entity)
{
base.StartEntity(entity);
}
///
public override void DirtyEntity(EntityUid uid, MetaDataComponent? meta = null)
{
// Client only dirties during prediction
if (_gameTiming.InPrediction)
base.DirtyEntity(uid, meta);
}
public override void QueueDeleteEntity(EntityUid? uid)
{
if (uid == null)
return;
if (IsClientSide(uid.Value))
{
base.QueueDeleteEntity(uid);
return;
}
if (ShuttingDown)
return;
// Client-side entity deletion is not supported and will cause errors.
if (_client.RunLevel == ClientRunLevel.Connected || _client.RunLevel == ClientRunLevel.InGame)
LogManager.RootSawmill.Error($"Predicting the queued deletion of a networked entity: {ToPrettyString(uid.Value)}. Trace: {Environment.StackTrace}");
}
///
public override void Dirty(EntityUid uid, IComponent component, MetaDataComponent? meta = null)
{
Dirty(new Entity(uid, component), meta);
}
///
public override void Dirty(Entity ent, MetaDataComponent? meta = null)
{
// Client only dirties during prediction
if (_gameTiming.InPrediction)
base.Dirty(ent, meta);
}
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
{
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
return base.ToPrettyString(uid);
}
public override void RaisePredictiveEvent(T msg)
{
var localPlayer = _playerManager.LocalPlayer;
DebugTools.AssertNotNull(localPlayer);
var sequence = _stateMan.SystemMessageDispatched(msg);
EntityNetManager?.SendSystemNetworkMessage(msg, sequence);
DebugTools.Assert(!_stateMan.IsPredictionEnabled || _gameTiming.InPrediction && _gameTiming.IsFirstTimePredicted || _client.RunLevel != ClientRunLevel.Connected);
var eventArgs = new EntitySessionEventArgs(localPlayer!.Session);
EventBus.RaiseEvent(EventSource.Local, msg);
EventBus.RaiseEvent(EventSource.Local, new EntitySessionMessage(eventArgs, msg));
}
#region IEntityNetworkManager impl
public override IEntityNetworkManager EntityNetManager => this;
///
public event EventHandler