using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Prometheus;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
#if EXCEPTION_TOLERANCE
using Robust.Shared.Exceptions;
#endif
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Server.GameObjects
{
///
/// Manager for entities -- controls things like template loading and instantiation
///
[UsedImplicitly] // DI Container
public sealed class ServerEntityManager : EntityManager, IServerEntityManagerInternal
{
private static readonly Gauge EntitiesCount = Metrics.CreateGauge(
"robust_entities_count",
"Amount of alive entities.");
[Dependency] private readonly IReplayRecordingManager _replay = default!;
[Dependency] private readonly IServerNetManager _networkManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
#if EXCEPTION_TOLERANCE
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
#endif
private ISawmill _netEntSawmill = default!;
private EntityQuery _actorQuery;
public override void Initialize()
{
_netEntSawmill = LogManager.GetSawmill("net.ent");
SetupNetworking();
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
base.Initialize();
}
public override void Startup()
{
base.Startup();
_actorQuery = GetEntityQuery();
}
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
{
return AllocEntity(prototype, out _);
}
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
{
LoadEntity(entity, context);
}
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, EntityPrototype? prototype, IEntityLoadContext? context)
{
LoadEntity(entity, context, prototype);
}
void IServerEntityManagerInternal.FinishEntityInitialization(EntityUid entity, MetaDataComponent? meta)
{
InitializeEntity(entity, meta);
}
void IServerEntityManagerInternal.FinishEntityStartup(EntityUid entity)
{
StartEntity(entity);
}
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
{
if (prototypeName == null)
return base.CreateEntity(prototypeName, out metadata, context);
if (!PrototypeManager.TryIndex(prototypeName, out var prototype))
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
var entity = base.CreateEntity(prototype, out metadata, context);
// At this point in time, all data configure on the entity *should* be purely from the prototype.
// As such, we can reset the modified ticks to Zero,
// which indicates "not different from client's own deserialization".
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
ClearTicks(entity, prototype);
return entity;
}
private void ClearTicks(EntityUid entity, EntityPrototype prototype)
{
foreach (var (netId, component) in GetNetComponents(entity))
{
// Make sure to ONLY get components that are defined in the prototype.
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
// And those aren't guaranteed to exist on the client, so don't clear them.
var compName = ComponentFactory.GetComponentName(netId);
if (prototype.Components.ContainsKey(compName))
component.ClearTicks();
}
}
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null)
{
_actorQuery.TryGetComponent(uid, out ActorComponent? actor);
return base.ToPrettyString(uid) with { Session = actor?.PlayerSession };
}
#region IEntityNetworkManager impl
public override IEntityNetworkManager EntityNetManager => this;
///
public event EventHandler