Merge ActorSystem and IPlayerManager (#4530)

This commit is contained in:
Leon Friedrich
2023-11-11 12:50:21 +11:00
committed by GitHub
parent 14cc273997
commit 58e0b62145
22 changed files with 331 additions and 310 deletions

View File

@@ -4,6 +4,7 @@ using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Player;
namespace Robust.Client.GameObjects;

View File

@@ -1,6 +1,5 @@
using System.Buffers;
using System.Collections.Generic;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
@@ -8,6 +7,7 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Dynamics.Contacts;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Robust.Client.Physics;

View File

@@ -15,15 +15,22 @@ public interface IPlayerManager : ISharedPlayerManager
event Action? PlayerListUpdated;
/// <summary>
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity. See also <see cref="LocalPlayerAttachedEvent"/>
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets attached to a new entity, or when the local
/// session gets updated. See also <see cref="LocalPlayerAttachedEvent"/>
/// </summary>
event Action<EntityUid>? LocalPlayerAttached;
/// <summary>
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from new entity. See also <see cref="LocalPlayerDetachedEvent"/>
/// Invoked when <see cref="ISharedPlayerManager.LocalSession"/> gets detached from an entity, or when the local
/// session gets updated. See also <see cref="LocalPlayerDetachedEvent"/>
/// </summary>
event Action<EntityUid>? LocalPlayerDetached;
/// <summary>
/// Invoked whenever <see cref="ISharedPlayerManager.LocalSession"/> changes.
/// </summary>
event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged;
void ApplyPlayerStates(IReadOnlyCollection<SessionState> list);
/// <summary>
@@ -38,34 +45,8 @@ public interface IPlayerManager : ISharedPlayerManager
/// </summary>
void SetupMultiplayer(INetChannel channel);
void SetLocalSession(ICommonSession session);
[Obsolete("Use LocalSession instead")]
LocalPlayer? LocalPlayer { get;}
}
/// <summary>
/// ECS event that gets raised when the local player gets attached to a new entity. The event is both broadcast and
/// raised directed at the new entity.
/// </summary>
public sealed class LocalPlayerAttachedEvent : EntityEventArgs
{
public LocalPlayerAttachedEvent(EntityUid entity)
{
Entity = entity;
}
public EntityUid Entity { get; }
}
/// <summary>
/// ECS event that gets raised when the local player gets detached from an entity. The event is both broadcast and
/// raised directed at the new entity.
/// </summary>
public sealed class LocalPlayerDetachedEvent : EntityEventArgs
{
public LocalPlayerDetachedEvent(EntityUid entity)
{
Entity = entity;
}
public EntityUid Entity { get; }
}

View File

@@ -42,12 +42,13 @@ namespace Robust.Client.Player
/// <inheritdoc />
public override int MaxPlayers => _client.GameInfo?.ServerMaxPlayers ?? -1;
public LocalPlayer? LocalPlayer { get; set; }
public LocalPlayer? LocalPlayer { get; private set; }
public event Action<SessionStatusEventArgs>? LocalStatusChanged;
public event Action? PlayerListUpdated;
public event Action<EntityUid>? LocalPlayerDetached;
public event Action<EntityUid>? LocalPlayerAttached;
public event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged;
/// <inheritdoc />
public override void Initialize(int maxPlayers)
@@ -64,22 +65,14 @@ namespace Robust.Client.Player
LocalStatusChanged?.Invoke(e);
}
/// <inheritdoc />
public override void Startup()
{
if (LocalSession == null)
throw new InvalidOperationException("LocalSession cannot be null");
LocalPlayer = new LocalPlayer(LocalSession);
base.Startup();
}
public void SetupSinglePlayer(string name)
{
if (LocalSession != null)
throw new InvalidOperationException($"Player manager already running?");
LocalSession = CreateAndAddSession(default, name);
var session = CreateAndAddSession(default, name);
session.ClientSide = true;
SetLocalSession(session);
Startup();
PlayerListUpdated?.Invoke();
}
@@ -89,18 +82,44 @@ namespace Robust.Client.Player
if (LocalSession != null)
throw new InvalidOperationException($"Player manager already running?");
var session = CreateAndAddSession(channel.UserId, channel.UserName);
session.Channel = channel;
LocalSession = session;
SetLocalSession(CreateAndAddSession(channel));
Startup();
_network.ClientSendMessage(new MsgPlayerListReq());
}
public void SetLocalSession(ICommonSession? session)
{
if (session == LocalSession)
return;
var old = LocalSession;
if (old?.AttachedEntity is {} oldUid)
{
LocalSession = null;
LocalPlayer = null;
Sawmill.Info($"Detaching local player from {EntManager.ToPrettyString(oldUid)}.");
EntManager.EventBus.RaiseLocalEvent(oldUid, new LocalPlayerDetachedEvent(oldUid), true);
LocalPlayerDetached?.Invoke(oldUid);
}
LocalSession = session;
LocalPlayer = session == null ? null : new LocalPlayer(session);
Sawmill.Info($"Changing local session from {old?.ToString() ?? "null"} to {session?.ToString() ?? "null"}.");
LocalSessionChanged?.Invoke((old, LocalSession));
if (session?.AttachedEntity is {} newUid)
{
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(newUid)}.");
EntManager.EventBus.RaiseLocalEvent(newUid, new LocalPlayerAttachedEvent(newUid), true);
LocalPlayerAttached?.Invoke(newUid);
}
}
/// <inheritdoc />
public override void Shutdown()
{
if (LocalSession != null)
SetAttachedEntity(LocalSession, null);
SetAttachedEntity(LocalSession, null, out _);
LocalPlayer = null;
LocalSession = null;
_pendingStates.Clear();
@@ -108,16 +127,21 @@ namespace Robust.Client.Player
PlayerListUpdated?.Invoke();
}
public override void SetAttachedEntity(ICommonSession session, EntityUid? uid)
public override bool SetAttachedEntity(ICommonSession? session, EntityUid? uid, out ICommonSession? kicked, bool force = false)
{
kicked = null;
if (session == null)
return false;
if (session.AttachedEntity == uid)
return;
return true;
var old = session.AttachedEntity;
base.SetAttachedEntity(session, uid);
if (!base.SetAttachedEntity(session, uid, out kicked, force))
return false;
if (session != LocalSession)
return;
return true;
if (old.HasValue)
{
@@ -129,13 +153,13 @@ namespace Robust.Client.Player
if (uid == null)
{
Sawmill.Info($"Local player is no longer attached to any entity.");
return;
return true;
}
if (!EntManager.EntityExists(uid))
{
Sawmill.Error($"Attempted to attach player to non-existent entity {uid}!");
return;
return true;
}
if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye))
@@ -148,6 +172,7 @@ namespace Robust.Client.Player
Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(uid)}.");
EntManager.EventBus.RaiseLocalEvent(uid.Value, new LocalPlayerAttachedEvent(uid.Value), true);
LocalPlayerAttached?.Invoke(uid.Value);
return true;
}
public void ApplyPlayerStates(IReadOnlyCollection<SessionState> list)
@@ -193,7 +218,7 @@ namespace Robust.Client.Player
_pendingStates.Remove(state.UserId);
}
SetAttachedEntity(LocalSession, uid);
SetAttachedEntity(LocalSession, uid, out _, true);
SetStatus(LocalSession, state.Status);
}
@@ -233,11 +258,10 @@ namespace Robust.Client.Player
{
// This is a new userid, so we create a new session.
DebugTools.Assert(state.UserId != LocalPlayer?.UserId);
var newSession = CreateAndAddSession(state.UserId, state.Name);
var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name);
newSession.Ping = state.Ping;
newSession.Name = state.Name;
SetStatus(newSession, state.Status);
SetAttachedEntity(newSession, controlled);
SetAttachedEntity(newSession, controlled, out _, true);
dirty = true;
continue;
}
@@ -256,7 +280,7 @@ namespace Robust.Client.Player
local.Name = state.Name;
local.Ping = state.Ping;
SetStatus(local, state.Status);
SetAttachedEntity(local, controlled);
SetAttachedEntity(local, controlled, out _, true);
}
// Remove old users. This only works if the provided state is a list of all players
@@ -264,10 +288,12 @@ namespace Robust.Client.Player
{
foreach (var oldUser in InternalSessions.Keys.ToArray())
{
// clear slot, player left
if (users.Contains(oldUser))
continue;
if (InternalSessions[oldUser].ClientSide)
continue;
DebugTools.Assert(oldUser != LocalUser
|| LocalUser == null
|| LocalUser == default(NetUserId),

View File

@@ -1,189 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Robust.Server.GameObjects
{
/// <summary>
/// System that handles players being attached/detached from entities.
/// </summary>
[UsedImplicitly]
public sealed class ActorSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
}
/// <summary>
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
/// </summary>
/// <param name="uid">The entity to attach the player to</param>
/// <param name="player">The player to attach to the entity</param>
/// <param name="force">Whether to kick any existing players from the entity</param>
/// <returns>Whether the attach succeeded, or not.</returns>
public bool Attach(EntityUid? uid, ICommonSession player, bool force = false)
{
return Attach(uid, player, false, out _);
}
/// <summary>
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
/// </summary>
/// <param name="entity">The entity to attach the player to</param>
/// <param name="player">The player to attach to the entity</param>
/// <param name="force">Whether to kick any existing players from the entity</param>
/// <param name="forceKicked">The player that was forcefully kicked, or null.</param>
/// <returns>Whether the attach succeeded, or not.</returns>
public bool Attach(EntityUid? entity, ICommonSession player, bool force, out ICommonSession? forceKicked)
{
// Null by default.
forceKicked = null;
if (player.AttachedEntity == entity)
{
DebugTools.Assert(entity == null || HasComp<ActorComponent>(entity));
return true;
}
if (entity is not { } uid)
return Detach(player);
// Cannot attach to a deleted, nonexisting or terminating entity.
if (TerminatingOrDeleted(uid))
return false;
// Check if there was a player attached to the entity already...
if (TryComp(uid, out ActorComponent? actor))
{
// If we're not forcing the attach, this fails.
if (!force)
return false;
// Set the event's force-kicked session before detaching it.
forceKicked = actor.PlayerSession;
RemComp(uid, actor);
DebugTools.AssertNull(forceKicked.AttachedEntity);
}
// Detach from the currently attached entity.
Detach(player);
// We add the actor component.
actor = EntityManager.AddComponent<ActorComponent>(uid);
EntityManager.EnsureComponent<EyeComponent>(uid);
actor.PlayerSession = player;
_playerManager.SetAttachedEntity(player, uid);
DebugTools.Assert(player.AttachedEntity == entity);
// The player is fully attached now, raise an event!
RaiseLocalEvent(uid, new PlayerAttachedEvent(uid, player, forceKicked), true);
return true;
}
/// <summary>
/// Detaches an attached session from the entity, if any.
/// </summary>
/// <param name="entity">The entity player sessions will be detached from.</param>
/// <returns>Whether any player session was detached.</returns>
public bool Detach(EntityUid uid, ActorComponent? actor = null)
{
if (!Resolve(uid, ref actor, false))
return false;
RemComp(uid, actor);
return true;
}
/// <summary>
/// Detaches this player from its attached entity, if any.
/// </summary>
/// <param name="player">The player session that will be detached from any attached entities.</param>
/// <returns>Whether the player is now detached from any entities.
/// This returns true if the player wasn't attached to any entity.</returns>
public bool Detach(ICommonSession player, ActorComponent? actor = null)
{
var uid = player.AttachedEntity;
if (uid == null)
return true;
if (!Resolve(uid.Value, ref actor, false))
{
Log.Error($"Player {player} was attached to a deleted entity?");
((CommonSession) player).AttachedEntity = null;
return true;
}
RemComp(uid.Value, actor);
DebugTools.AssertNull(player.AttachedEntity);
return false;
}
private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args)
{
_playerManager.SetAttachedEntity(component.PlayerSession, null);
// The player is fully detached now that the component has shut down.
RaiseLocalEvent(entity, new PlayerDetachedEvent(entity, component.PlayerSession), true);
}
public bool TryGetActorFromUserId(NetUserId? userId, [NotNullWhen(true)] out ICommonSession? actor, out EntityUid? actorEntity)
{
actor = null;
actorEntity = null;
if (userId != null)
{
if (!_playerManager.TryGetSessionById(userId.Value, out actor))
return false;
actorEntity = actor.AttachedEntity;
}
return actor != null;
}
}
/// <summary>
/// Event for when a player has been attached to an entity.
/// </summary>
public sealed class PlayerAttachedEvent : EntityEventArgs
{
public EntityUid Entity { get; }
public ICommonSession Player { get; }
/// <summary>
/// The player session that was forcefully kicked from the entity, if any.
/// </summary>
public ICommonSession? Kicked { get; }
public PlayerAttachedEvent(EntityUid entity, ICommonSession player, ICommonSession? kicked = null)
{
Entity = entity;
Player = player;
Kicked = kicked;
}
}
/// <summary>
/// Event for when a player has been detached from an entity.
/// </summary>
public sealed class PlayerDetachedEvent : EntityEventArgs
{
public EntityUid Entity { get; }
public ICommonSession Player { get; }
public PlayerDetachedEvent(EntityUid entity, ICommonSession player)
{
Entity = entity;
Player = player;
}
}
}

View File

@@ -1,5 +1,4 @@
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
namespace Robust.Server.GameObjects;

View File

@@ -1,6 +1,7 @@
using Robust.Server.GameStates;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Player;
namespace Robust.Server.GameObjects;

View File

@@ -80,9 +80,7 @@ namespace Robust.Server.Player
/// <param name="args"></param>
private void NewSession(object? sender, NetChannelArgs args)
{
var session = CreateAndAddSession(args.Channel.UserId, args.Channel.UserName);
session.Channel = args.Channel;
CreateAndAddSession(args.Channel);
PlayerCountMetric.Set(PlayerCount);
// Synchronize base time.
var msgTimeBase = new MsgSyncTimeBase();
@@ -106,8 +104,7 @@ namespace Robust.Server.Player
DebugTools.Assert(session.Channel == args.Channel);
SetStatus(session, SessionStatus.Disconnected);
if (session.AttachedEntity != null)
EntManager.System<ActorSystem>().Detach(session.AttachedEntity.Value);
SetAttachedEntity(session, null, out _, true);
var viewSys = EntManager.System<ViewSubscriberSystem>();
foreach (var eye in session.ViewSubscriptions.ToArray())

View File

@@ -0,0 +1,23 @@
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Shared.Player;
/// <summary>
/// System that handles <see cref="ActorComponent"/>.
/// </summary>
public sealed class ActorSystem : EntitySystem
{
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
}
private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args)
{
_playerManager.SetAttachedEntity(component.PlayerSession, null);
}
}

View File

@@ -17,10 +17,10 @@ internal sealed class CommonSession : ICommonSession
public NetUserId UserId { get; }
[ViewVariables]
public string Name { get; set; } = "<Unknown>";
public string Name { get; internal set; } = "<Unknown>";
[ViewVariables]
public short Ping { get; set; }
public short Ping { get; internal set; }
[ViewVariables]
public DateTime ConnectedTime { get; set; }
@@ -34,6 +34,8 @@ internal sealed class CommonSession : ICommonSession
[ViewVariables]
public SessionData Data { get; }
public bool ClientSide { get; set; }
[ViewVariables]
public INetChannel Channel { get; set; } = default!;
@@ -54,4 +56,4 @@ internal sealed class CommonSession : ICommonSession
Name = name;
Data = data;
}
}
}

View File

@@ -0,0 +1,63 @@
using Robust.Shared.GameObjects;
namespace Robust.Shared.Player;
/// <summary>
/// Event that gets raised when a player has been attached to an entity. This event is both raised directed at the
/// entity and broadcast.
/// </summary>
public sealed class PlayerAttachedEvent : EntityEventArgs
{
public readonly EntityUid Entity;
public readonly ICommonSession Player;
public PlayerAttachedEvent(EntityUid entity, ICommonSession player)
{
Entity = entity;
Player = player;
}
}
/// <summary>
/// Event that gets raised when a player has been detached from an entity. This event is both raised directed at the
/// entity and broadcast.
/// </summary>
public sealed class PlayerDetachedEvent : EntityEventArgs
{
public readonly EntityUid Entity;
public readonly ICommonSession Player;
public PlayerDetachedEvent(EntityUid entity, ICommonSession player)
{
Entity = entity;
Player = player;
}
}
/// <summary>
/// Variant of <see cref="PlayerAttachedEvent"/> that gets raised by the client when the local session gets attached to
/// a new entity. This event will also get raised if the local session changes.
/// </summary>
public sealed class LocalPlayerAttachedEvent : EntityEventArgs
{
public readonly EntityUid Entity;
public LocalPlayerAttachedEvent(EntityUid entity)
{
Entity = entity;
}
}
/// <summary>
/// Variant of <see cref="PlayerDetachedEvent"/> that gets raised by the client when the local session gets attached to
/// a new entity. This event will also get raised if the local session changes.
/// </summary>
public sealed class LocalPlayerDetachedEvent : EntityEventArgs
{
public readonly EntityUid Entity;
public LocalPlayerDetachedEvent(EntityUid entity)
{
Entity = entity;
}
}

View File

@@ -30,12 +30,12 @@ public interface ICommonSession
/// <summary>
/// Current name of this player.
/// </summary>
string Name { get; set; }
string Name { get; }
/// <summary>
/// Current connection latency of this session from the server to their client.
/// </summary>
short Ping { get; internal set; }
short Ping { get; }
/// <summary>
/// The current network channel for this session.
@@ -43,8 +43,9 @@ public interface ICommonSession
/// <remarks>
/// On the Server every player has a network channel,
/// on the Client only the LocalPlayer has a network channel, and that channel points to the server.
/// Unless you know what you are doing, you shouldn't be modifying this directly.
/// </remarks>
INetChannel Channel { get; }
INetChannel Channel { get; set; }
LoginType AuthType { get; }
@@ -67,4 +68,10 @@ public interface ICommonSession
[Obsolete("Just use the Channel field instead.")]
INetChannel ConnectedClient => Channel;
}
/// <summary>
/// If true, this indicates that this is a client-side session, and should be ignored when applying a server's
/// game state.
/// </summary>
bool ClientSide { get; set; }
}

View File

@@ -87,7 +87,7 @@ public interface ISharedPlayerManager
/// <summary>
/// Attempts to get the session with the given <see cref="NetUserId"/>.
/// </summary>
bool TryGetSessionById(NetUserId user, [NotNullWhen(true)] out ICommonSession? session);
bool TryGetSessionById([NotNullWhen(true)] NetUserId? user, [NotNullWhen(true)] out ICommonSession? session);
/// <summary>
/// Attempts to get the session with the given <see cref="ICommonSession.Name"/>.
@@ -121,16 +121,49 @@ public interface ISharedPlayerManager
void RemoveSession(ICommonSession session, bool removeData = false);
void RemoveSession(NetUserId user, bool removeData = false);
ICommonSession CreateAndAddSession(INetChannel channel);
ICommonSession CreateAndAddSession(NetUserId user, string name);
/// <summary>
/// Updates a session's <see cref="ICommonSession.AttachedEntity"/>
/// Sets a session's attached entity, optionally kicking any sessions already attached to it.
/// </summary>
void SetAttachedEntity(ICommonSession session, EntityUid? uid);
/// <param name="session">The player whose attached entity should get updated</param>
/// <param name="entity">The entity to attach the player to, if any.</param>
/// <param name="force">Whether to kick any existing players that are already attached to the entity</param>
/// <param name="kicked">The player that was forcefully kicked, if any.</param>
/// <returns>Whether the attach succeeded, or not.</returns>
bool SetAttachedEntity(
[NotNullWhen(true)] ICommonSession? session,
EntityUid? entity,
out ICommonSession? kicked,
bool force = false);
/// <summary>
/// Sets a session's attached entity, optionally kicking any sessions already attached to it.
/// </summary>
/// <param name="session">The player whose attached entity should get updated</param>
/// <param name="entity">The entity to attach the player to, if any.</param>
/// <param name="force">Whether to kick any existing players that are already attached to the entity</param>
/// <returns>Whether the attach succeeded, or not.</returns>
bool SetAttachedEntity([NotNullWhen(true)] ICommonSession? session, EntityUid? entity, bool force = false)
=> SetAttachedEntity(session, entity, out _, force);
/// <summary>
/// Updates a session's <see cref="ICommonSession.Status"/>
/// </summary>
void SetStatus(ICommonSession session, SessionStatus status);
/// <summary>
/// Updates a session's <see cref="ICommonSession.Ping"/>
/// </summary>
void SetPing(ICommonSession session, short ping);
/// <summary>
/// Updates a session's <see cref="ICommonSession.Name"/>
/// </summary>
public void SetName(ICommonSession session, string name);
/// <summary>
/// Set the session's status to <see cref="SessionStatus.InGame"/>.
/// </summary>
@@ -138,8 +171,4 @@ public interface ISharedPlayerManager
[Obsolete("Use GetSessionById()")]
ICommonSession GetSessionByUserId(NetUserId user) => GetSessionById(user);
[Obsolete("Use TryGetSessionById()")]
bool TryGetSessionByUserId(NetUserId user, [NotNullWhen(true)] out ICommonSession? session)
=> TryGetSessionById(user, out session);
}

View File

@@ -50,12 +50,18 @@ internal abstract partial class SharedPlayerManager
}
}
public bool TryGetSessionById(NetUserId user, [NotNullWhen(true)] out ICommonSession? session)
public bool TryGetSessionById([NotNullWhen(true)] NetUserId? user, [NotNullWhen(true)] out ICommonSession? session)
{
if (user == null)
{
session = null;
return false;
}
Lock.EnterReadLock();
try
{
return InternalSessions.TryGetValue(user, out session);
return InternalSessions.TryGetValue(user.Value, out session);
}
finally
{
@@ -93,7 +99,14 @@ internal abstract partial class SharedPlayerManager
return new CommonSession(user, name, data);
}
internal CommonSession CreateAndAddSession(NetUserId user, string name)
public ICommonSession CreateAndAddSession(INetChannel channel)
{
var session = CreateAndAddSession(channel.UserId, channel.UserName);
session.Channel = channel;
return session;
}
public ICommonSession CreateAndAddSession(NetUserId user, string name)
{
Lock.EnterWriteLock();
CommonSession session;
@@ -134,13 +147,78 @@ internal abstract partial class SharedPlayerManager
}
}
public virtual void SetAttachedEntity(ICommonSession session, EntityUid? uid)
/// <inheritdoc cref="ISharedPlayerManager.SetAttachedEntity"/>
public virtual bool SetAttachedEntity(
[NotNullWhen(true)] ICommonSession? session,
EntityUid? uid,
out ICommonSession? kicked,
bool force = false)
{
kicked = null;
if (session == null)
return false;
if (session.AttachedEntity == uid)
{
DebugTools.Assert(uid == null || EntManager.HasComponent<ActorComponent>(uid));
return true;
}
if (uid != null)
return Attach(session, uid.Value, out kicked, force);
Detach(session);
return true;
}
private void Detach(ICommonSession session)
{
if (session.AttachedEntity is not {} uid)
return;
((CommonSession) session).AttachedEntity = uid;
((CommonSession) session).AttachedEntity = null;
UpdateState(session);
if (EntManager.TryGetComponent(uid, out ActorComponent? actor) && actor.LifeStage <= ComponentLifeStage.Running)
{
actor.PlayerSession = default!;
EntManager.RemoveComponent(uid, actor);
}
EntManager.EventBus.RaiseLocalEvent(uid, new PlayerDetachedEvent(uid, session), true);
}
private bool Attach(ICommonSession session, EntityUid uid, out ICommonSession? kicked, bool force = false)
{
kicked = null;
if (!EntManager.TryGetComponent(uid, out MetaDataComponent? meta))
return false;
if (meta.EntityLifeStage >= EntityLifeStage.Terminating)
return false;
if (EntManager.EnsureComponent<ActorComponent>(uid, out var actor))
{
// component already existed.
DebugTools.AssertNotNull(actor.PlayerSession);
if (!force)
return false;
kicked = actor.PlayerSession;
Detach(kicked);
}
if (_netMan.IsServer)
EntManager.EnsureComponent<EyeComponent>(uid);
if (session.AttachedEntity != null)
Detach(session);
((CommonSession) session).AttachedEntity = uid;
actor.PlayerSession = session;
UpdateState(session);
EntManager.EventBus.RaiseLocalEvent(uid, new PlayerAttachedEvent(uid, session), true);
return true;
}
public void SetStatus(ICommonSession session, SessionStatus status)
@@ -155,6 +233,18 @@ internal abstract partial class SharedPlayerManager
PlayerStatusChanged?.Invoke(this, new SessionStatusEventArgs(session, old, status));
}
public void SetPing(ICommonSession session, short ping)
{
((CommonSession) session).Ping = ping;
UpdateState(session);
}
public void SetName(ICommonSession session, string name)
{
((CommonSession) session).Name = name;
UpdateState(session);
}
public void JoinGame(ICommonSession session)
{
// This currently just directly sets the session's status, as this was the old behaviour.

View File

@@ -14,6 +14,7 @@ internal abstract partial class SharedPlayerManager : ISharedPlayerManager
[Dependency] protected readonly IEntityManager EntManager = default!;
[Dependency] protected readonly ILogManager LogMan = default!;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] private readonly INetManager _netMan = default!;
protected ISawmill Sawmill = default!;
@@ -26,7 +27,7 @@ internal abstract partial class SharedPlayerManager : ISharedPlayerManager
public int PlayerCount => InternalSessions.Count;
[ViewVariables]
public ICommonSession? LocalSession { get; set; }
public ICommonSession? LocalSession { get; protected set; }
[ViewVariables]
public NetUserId? LocalUser => LocalSession?.UserId;

View File

@@ -1,7 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
@@ -81,7 +80,7 @@ public sealed class DefaultEntityTest : RobustIntegrationTest
coords = new(map, default);
var playerUid = sEntMan.SpawnEntity(null, coords);
player = sEntMan.GetNetEntity(playerUid);
sEntMan.System<ActorSystem>().Attach(playerUid, session);
server.PlayerMan.SetAttachedEntity(session, playerUid);
});
for (int i = 0; i < 10; i++)

View File

@@ -4,15 +4,13 @@ using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.GameStates;
using Robust.Client.Timing;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using cIPlayerManager = Robust.Client.Player.IPlayerManager;
using sIPlayerManager = Robust.Server.Player.IPlayerManager;
namespace Robust.UnitTesting.Server.GameStates;
@@ -33,13 +31,13 @@ public sealed class PvsReEntryTest : RobustIntegrationTest
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<sIPlayerManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xforms = sEntMan.System<SharedTransformSystem>();
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var cPlayerMan = client.ResolveDependency<cIPlayerManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
@@ -87,7 +85,7 @@ public sealed class PvsReEntryTest : RobustIntegrationTest
// Attach player.
var session = sPlayerMan.Sessions.First();
sEntMan.System<ActorSystem>().Attach(playerUid, session);
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
});
@@ -106,7 +104,7 @@ public sealed class PvsReEntryTest : RobustIntegrationTest
{
Assert.That(cEntMan.TryGetEntityData(entity, out _, out meta));
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
Assert.That(cPlayerMan.LocalPlayer?.ControlledEntity, Is.EqualTo(cPlayerUid));
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
});

View File

@@ -2,15 +2,13 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using cIPlayerManager = Robust.Client.Player.IPlayerManager;
using sIPlayerManager = Robust.Server.Player.IPlayerManager;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
@@ -30,12 +28,12 @@ public sealed class PvsSystemTests : RobustIntegrationTest
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<sIPlayerManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xforms = sEntMan.System<SharedTransformSystem>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var cPlayerMan = client.ResolveDependency<cIPlayerManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
@@ -76,7 +74,7 @@ public sealed class PvsSystemTests : RobustIntegrationTest
// Attach player.
var session = sPlayerMan.Sessions.First();
sEntMan.System<ActorSystem>().Attach(player, session);
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
@@ -89,7 +87,7 @@ public sealed class PvsSystemTests : RobustIntegrationTest
// Check player got properly attached
await client.WaitPost(() =>
{
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity);
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player)));
});

View File

@@ -72,7 +72,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
// Setup PVS
sEntManager.AddComponent<EyeComponent>(entityUid);
var player = sPlayerManager.Sessions.First();
sEntManager.System<ActorSystem>().Attach(entityUid, player);
server.PlayerMan.SetAttachedEntity(player, entityUid);
sPlayerManager.JoinGame(player);
});
@@ -200,7 +200,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
// Setup PVS
sEntManager.AddComponent<EyeComponent>(sEntityUid);
var player = sPlayerManager.Sessions.First();
sEntManager.System<ActorSystem>().Attach(sEntityUid, player);
server.PlayerMan.SetAttachedEntity(player, sEntityUid);
sPlayerManager.JoinGame(player);
});

View File

@@ -2,7 +2,6 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
@@ -73,7 +72,7 @@ public sealed partial class ComponentStateTests : RobustIntegrationTest
// Attach player.
player = server.EntMan.Spawn();
var session = server.PlayerMan.Sessions.First();
server.System<ActorSystem>().Attach(player, session);
server.PlayerMan.SetAttachedEntity(session, player);
server.PlayerMan.JoinGame(session);
// Spawn test entities.
@@ -209,7 +208,7 @@ public sealed partial class ComponentStateTests : RobustIntegrationTest
// Attach player.
player = server.EntMan.Spawn();
var session = server.PlayerMan.Sessions.First();
server.System<ActorSystem>().Attach(player, session);
server.PlayerMan.SetAttachedEntity(session, player);
server.PlayerMan.JoinGame(session);
// Spawn test entities.

View File

@@ -2,15 +2,13 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using cIPlayerManager = Robust.Client.Player.IPlayerManager;
using sIPlayerManager = Robust.Server.Player.IPlayerManager;
using Robust.Shared.Player;
// ReSharper disable AccessToStaticMemberViaDerivedType
@@ -37,8 +35,8 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var cPlayerMan = client.ResolveDependency<cIPlayerManager>();
var sPlayerMan = server.ResolveDependency<sIPlayerManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xformSys = sEntMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
@@ -85,7 +83,7 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
var coords = new EntityCoordinates(grid1, new Vector2(0.5f, 0.5f));
player = sEntMan.SpawnEntity(null, coords);
var session = sPlayerMan.Sessions.First();
sEntMan.System<ActorSystem>().Attach(player, session);
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
@@ -94,7 +92,7 @@ public sealed class DeletionNetworkingTests : RobustIntegrationTest
// Check player got properly attached
await client.WaitPost(() =>
{
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity);
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player)));
});

View File

@@ -2,7 +2,6 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
@@ -14,8 +13,7 @@ using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using cIPlayerManager = Robust.Client.Player.IPlayerManager;
using sIPlayerManager = Robust.Server.Player.IPlayerManager;
using Robust.Shared.Player;
// ReSharper disable AccessToStaticMemberViaDerivedType
@@ -42,8 +40,8 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var cPlayerMan = client.ResolveDependency<cIPlayerManager>();
var sPlayerMan = server.ResolveDependency<sIPlayerManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var fixturesSystem = sEntMan.EntitySysManager.GetEntitySystem<FixtureSystem>();
var physicsSystem = sEntMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
@@ -91,7 +89,7 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
// Attach player.
var session = sPlayerMan.Sessions.First();
sEntMan.System<ActorSystem>().Attach(player, session);
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
@@ -106,7 +104,7 @@ public sealed class BroadphaseNetworkingTest : RobustIntegrationTest
// Check player got properly attached
await client.WaitPost(() =>
{
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity);
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
Assert.That(ent, Is.EqualTo(playerNet));
});