diff --git a/Robust.Shared.Scripting/ScriptGlobalsShared.cs b/Robust.Shared.Scripting/ScriptGlobalsShared.cs index 0682ed80d..5b7fc60da 100644 --- a/Robust.Shared.Scripting/ScriptGlobalsShared.cs +++ b/Robust.Shared.Scripting/ScriptGlobalsShared.cs @@ -9,6 +9,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Map.Components; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; @@ -260,6 +261,7 @@ namespace Robust.Shared.Scripting return true; // Do as I say! } + public NetUserId? User => null; public ICommonSession? Session => null; public void WriteLine(string line) diff --git a/Robust.Shared/Toolshed/Commands/Entities/DoCommand.cs b/Robust.Shared/Toolshed/Commands/Entities/DoCommand.cs index f764e584c..dde19d7ad 100644 --- a/Robust.Shared/Toolshed/Commands/Entities/DoCommand.cs +++ b/Robust.Shared/Toolshed/Commands/Entities/DoCommand.cs @@ -17,7 +17,7 @@ internal sealed class DoCommand : ToolshedCommand [PipedArgument] IEnumerable input, [CommandArgument] string command) { - if (ctx is not OldShellInvocationContext { } reqCtx) + if (ctx is not OldShellInvocationContext { } reqCtx || reqCtx.Shell == null) { throw new NotImplementedException("do can only be executed in a shell invocation context. Some commands like emplace provide their own context."); } diff --git a/Robust.Shared/Toolshed/Commands/Generic/EmplaceCommand.cs b/Robust.Shared/Toolshed/Commands/Generic/EmplaceCommand.cs index 49c063f97..c88df0185 100644 --- a/Robust.Shared/Toolshed/Commands/Generic/EmplaceCommand.cs +++ b/Robust.Shared/Toolshed/Commands/Generic/EmplaceCommand.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Robust.Shared.GameObjects; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Toolshed.Errors; using Robust.Shared.Toolshed.Syntax; @@ -50,6 +51,8 @@ internal record EmplaceContext(IInvocationContext Inner, T Value, IEntityMana public ICommonSession? Session => Inner.Session; public ToolshedManager Toolshed => Inner.Toolshed; + public NetUserId? User => Inner.User; + public ToolshedEnvironment Environment => Inner.Environment; public void WriteLine(string line) diff --git a/Robust.Shared/Toolshed/Commands/Generic/ReduceCommand.cs b/Robust.Shared/Toolshed/Commands/Generic/ReduceCommand.cs index 44c3ecf51..49a02eb84 100644 --- a/Robust.Shared/Toolshed/Commands/Generic/ReduceCommand.cs +++ b/Robust.Shared/Toolshed/Commands/Generic/ReduceCommand.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Toolshed.Errors; using Robust.Shared.Toolshed.Syntax; @@ -27,6 +28,8 @@ internal record ReduceContext(IInvocationContext Inner, T Value) : IInvocatio public ICommonSession? Session => Inner.Session; public ToolshedManager Toolshed => Inner.Toolshed; + public NetUserId? User => Inner.User; + public ToolshedEnvironment Environment => Inner.Environment; public void WriteLine(string line) diff --git a/Robust.Shared/Toolshed/IInvocationContext.cs b/Robust.Shared/Toolshed/IInvocationContext.cs index ac2a8b098..120762ff1 100644 --- a/Robust.Shared/Toolshed/IInvocationContext.cs +++ b/Robust.Shared/Toolshed/IInvocationContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Toolshed.Errors; using Robust.Shared.Utility; @@ -33,10 +34,15 @@ public interface IInvocationContext ToolshedEnvironment Environment { get; } /// - /// The session this context is for, if any. + /// The session for the , if any currently exists. /// ICommonSession? Session { get; } + /// + /// The session this context is for, if any. + /// + NetUserId? User { get; } + ToolshedManager Toolshed { get; } /// diff --git a/Robust.Shared/Toolshed/Invocation/OldShellInvocationContext.cs b/Robust.Shared/Toolshed/Invocation/OldShellInvocationContext.cs index e378d75ba..d6c0f997e 100644 --- a/Robust.Shared/Toolshed/Invocation/OldShellInvocationContext.cs +++ b/Robust.Shared/Toolshed/Invocation/OldShellInvocationContext.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Robust.Shared.Console; using Robust.Shared.IoC; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Toolshed.Errors; using Robust.Shared.Utility; @@ -18,29 +19,33 @@ internal sealed class OldShellInvocationContext : IInvocationContext private readonly List _errors = new(); /// - /// Old system's shell associated with this context + /// Old system's shell associated with this context. May be null if the player is currently disconnected. /// - public IConsoleShell Shell; + public IConsoleShell? Shell; public OldShellInvocationContext(IConsoleShell shell) { IoCManager.InjectDependencies(this); Shell = shell; + User = Session?.UserId; } /// - public ICommonSession? Session => Shell.Player; + public NetUserId? User { get; } + + /// + public ICommonSession? Session => Shell?.Player; /// public void WriteLine(string line) { - Shell.WriteLine(line); + Shell?.WriteLine(line); } /// public void WriteLine(FormattedMessage line) { - Shell.WriteLine(line); + Shell?.WriteLine(line); } /// diff --git a/Robust.Shared/Toolshed/ToolshedManager.cs b/Robust.Shared/Toolshed/ToolshedManager.cs index 07f2bb083..97d53883a 100644 --- a/Robust.Shared/Toolshed/ToolshedManager.cs +++ b/Robust.Shared/Toolshed/ToolshedManager.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; using Robust.Shared.Console; +using Robust.Shared.Enums; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Reflection; -using Robust.Shared.Timing; using Robust.Shared.Toolshed.Invocation; using Robust.Shared.Toolshed.Syntax; using Robust.Shared.Toolshed.TypeParsers; @@ -30,10 +26,16 @@ public sealed partial class ToolshedManager [Dependency] private readonly IEntityManager _entity = default!; [Dependency] private readonly IReflectionManager _reflection = default!; [Dependency] private readonly ILogManager _logManager = default!; +#if !CLIENT_SCRIPTING [Dependency] private readonly INetManager _net = default!; +#endif + [Dependency] private readonly ISharedPlayerManager _player = default!; + [Dependency] private readonly IConsoleHost _conHost = default!; private ISawmill _log = default!; + private Dictionary _contexts = new(); + /// /// If you're not an engine developer, you probably shouldn't call this. /// @@ -42,9 +44,28 @@ public sealed partial class ToolshedManager _log = _logManager.GetSawmill("toolshed"); InitializeParser(); + _player.PlayerStatusChanged += OnStatusChanged; } - private Dictionary _contexts = new(); + private void OnStatusChanged(object? sender, SessionStatusEventArgs e) + { + if (!_contexts.TryGetValue(e.Session.UserId, out var ctx)) + return; + + DebugTools.Assert(ctx.User == e.Session.UserId); + if (e.NewStatus == SessionStatus.Disconnected) + { + DebugTools.Assert(ctx.Session == e.Session); + ctx.Shell = null; + } + + if (e.NewStatus == SessionStatus.InGame) + { + DebugTools.AssertNull(ctx.Session); + DebugTools.AssertNull(ctx.Shell); + ctx.Shell = new ConsoleShell(_conHost, e.Session, false); + } + } /// /// Invokes a command as the given user. @@ -85,12 +106,12 @@ public sealed partial class ToolshedManager /// An input value to use, if any. /// The resulting value, if any. /// Invocation success. - /// - /// ToolshedManager toolshed = ...; - /// IConsoleShell ctx = ...; - /// // Now run some user provided command and get a result! - /// toolshed.InvokeCommand(ctx, userCommand, "my input value", out var result); - /// + /// + /// ToolshedManager toolshed = ...; + /// IConsoleShell ctx = ...; + /// // Now run some user provided command and get a result! + /// toolshed.InvokeCommand(ctx, userCommand, "my input value", out var result); + /// /// /// This will use the same IInvocationContext as the one used by the user for debug console commands. /// diff --git a/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs b/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs index 134fcfbf9..daf4e8846 100644 --- a/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs +++ b/Robust.UnitTesting/Shared/Toolshed/ToolshedTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using NUnit.Framework; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Toolshed; using Robust.Shared.Toolshed.Errors; @@ -102,6 +103,8 @@ public abstract class ToolshedTest : RobustIntegrationTest, IInvocationContext protected ICommonSession? InvocationSession { get; set; } + public NetUserId? User => Session?.UserId; + public ICommonSession? Session { get