mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
* ComponentNames are not sent over the network when components are created. * Removed ComponentStates array from EntityState, now the state is stored directly inside the CompChange struct. * Remove the unnecessary NetID property from ComponentState. * Remove Component.NetworkSynchronizeExistence. * Change GetNetComponents to return both the component and the component NetId. * Remove public usages of the Component.NetID property. * Adds the NetIDAttribute that can be applied to components. * Removed Component.NetID. * Revert changes to GetComponentState and how prediction works. * Adds component netID automatic generation. * Modifies ClientConsoleHost so that commands can be called before Initialize(). * Completely remove static NetIds. * Renamed NetIDAttribute to NetworkedComponentAttribute. * Fixing unit tests.
175 lines
6.4 KiB
C#
175 lines
6.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Prometheus;
|
|
using Robust.Client.GameStates;
|
|
using Robust.Client.Player;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Network.Messages;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Client.GameObjects
|
|
{
|
|
/// <summary>
|
|
/// Manager for entities -- controls things like template loading and instantiation
|
|
/// </summary>
|
|
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
|
{
|
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
|
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
|
|
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
|
|
|
|
public override void Initialize()
|
|
{
|
|
SetupNetworking();
|
|
ReceivedComponentMessage += (_, compMsg) => DispatchComponentMessage(compMsg);
|
|
ReceivedSystemMessage += (_, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
|
|
|
|
base.Initialize();
|
|
}
|
|
|
|
IEntity IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid? uid)
|
|
{
|
|
return base.CreateEntity(prototypeName, uid);
|
|
}
|
|
|
|
void IClientEntityManagerInternal.InitializeEntity(IEntity entity)
|
|
{
|
|
EntityManager.InitializeEntity((Entity)entity);
|
|
}
|
|
|
|
void IClientEntityManagerInternal.StartEntity(IEntity entity)
|
|
{
|
|
base.StartEntity((Entity)entity);
|
|
}
|
|
|
|
#region IEntityNetworkManager impl
|
|
|
|
public override IEntityNetworkManager EntityNetManager => this;
|
|
|
|
/// <inheritdoc />
|
|
public event EventHandler<NetworkComponentMessage>? ReceivedComponentMessage;
|
|
|
|
/// <inheritdoc />
|
|
public event EventHandler<object>? ReceivedSystemMessage;
|
|
|
|
private readonly PriorityQueue<(uint seq, MsgEntity msg)> _queue = new(new MessageTickComparer());
|
|
private uint _incomingMsgSequence = 0;
|
|
|
|
/// <inheritdoc />
|
|
public void SetupNetworking()
|
|
{
|
|
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
|
}
|
|
|
|
public override void TickUpdate(float frameTime, Histogram? histogram)
|
|
{
|
|
using (histogram?.WithLabels("EntityNet").NewTimer())
|
|
{
|
|
while (_queue.Count != 0 && _queue.Peek().msg.SourceTick <= _gameStateManager.CurServerTick)
|
|
{
|
|
var (_, msg) = _queue.Take();
|
|
// Logger.DebugS("net.ent", "Dispatching: {0}: {1}", seq, msg);
|
|
DispatchMsgEntity(msg);
|
|
}
|
|
}
|
|
|
|
base.TickUpdate(frameTime, histogram);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SendSystemNetworkMessage(EntityEventArgs message)
|
|
{
|
|
SendSystemNetworkMessage(message, default(uint));
|
|
}
|
|
|
|
public void SendSystemNetworkMessage(EntityEventArgs message, uint sequence)
|
|
{
|
|
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
|
msg.Type = EntityMessageType.SystemMessage;
|
|
msg.SystemMessage = message;
|
|
msg.SourceTick = _gameTiming.CurTick;
|
|
msg.Sequence = sequence;
|
|
|
|
_networkManager.ClientSendMessage(msg);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
|
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
|
|
{
|
|
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
|
|
|
|
if (!netId.HasValue)
|
|
throw new ArgumentException($"Component {component.Name} does not have a NetID.", nameof(component));
|
|
|
|
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
|
msg.Type = EntityMessageType.ComponentMessage;
|
|
msg.EntityUid = entity.Uid;
|
|
msg.NetId = netId.Value;
|
|
msg.ComponentMessage = message;
|
|
msg.SourceTick = _gameTiming.CurTick;
|
|
|
|
_networkManager.ClientSendMessage(msg);
|
|
}
|
|
|
|
private void HandleEntityNetworkMessage(MsgEntity message)
|
|
{
|
|
if (message.SourceTick <= _gameStateManager.CurServerTick)
|
|
{
|
|
DispatchMsgEntity(message);
|
|
return;
|
|
}
|
|
|
|
// MsgEntity is sent with ReliableOrdered so Lidgren guarantees ordering of incoming messages.
|
|
// We still need to store a sequence input number to ensure ordering remains consistent in
|
|
// the priority queue.
|
|
_queue.Add((++_incomingMsgSequence, message));
|
|
}
|
|
|
|
private void DispatchMsgEntity(MsgEntity message)
|
|
{
|
|
switch (message.Type)
|
|
{
|
|
case EntityMessageType.ComponentMessage:
|
|
ReceivedComponentMessage?.Invoke(this, new NetworkComponentMessage(message));
|
|
return;
|
|
|
|
case EntityMessageType.SystemMessage:
|
|
var msg = message.SystemMessage;
|
|
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
|
|
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalPlayer!.Session), msg)!;
|
|
ReceivedSystemMessage?.Invoke(this, msg);
|
|
ReceivedSystemMessage?.Invoke(this, sessionMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private sealed class MessageTickComparer : IComparer<(uint seq, MsgEntity msg)>
|
|
{
|
|
public int Compare((uint seq, MsgEntity msg) x, (uint seq, MsgEntity msg) y)
|
|
{
|
|
var cmp = y.msg.SourceTick.CompareTo(x.msg.SourceTick);
|
|
if (cmp != 0)
|
|
{
|
|
return cmp;
|
|
}
|
|
|
|
return y.seq.CompareTo(x.seq);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|