diff --git a/MSBuild/Robust.DefineConstants.targets b/MSBuild/Robust.DefineConstants.targets
index c84cfe691..780433cc3 100644
--- a/MSBuild/Robust.DefineConstants.targets
+++ b/MSBuild/Robust.DefineConstants.targets
@@ -23,7 +23,7 @@
$(DefineConstants);EXCEPTION_TOLERANCE
-
- $(DefineConstants);SCRIPTING
+
+ $(DefineConstants);CLIENT_SCRIPTING
diff --git a/MSBuild/Robust.Properties.targets b/MSBuild/Robust.Properties.targets
index 50d1f4672..ac9f732ed 100644
--- a/MSBuild/Robust.Properties.targets
+++ b/MSBuild/Robust.Properties.targets
@@ -27,8 +27,8 @@
python3
py -3
netcoreapp3.0
- True
-
- False
+ True
+
+ False
diff --git a/Robust.Client/ClientIoC.cs b/Robust.Client/ClientIoC.cs
index d99b00296..b050d1350 100644
--- a/Robust.Client/ClientIoC.cs
+++ b/Robust.Client/ClientIoC.cs
@@ -121,6 +121,7 @@ namespace Robust.Client
IoCManager.Register();
IoCManager.Register();
IoCManager.Register();
+ IoCManager.Register();
}
}
}
diff --git a/Robust.Client/Console/ClientConGroupController.cs b/Robust.Client/Console/ClientConGroupController.cs
index 7e414620a..833af52b3 100644
--- a/Robust.Client/Console/ClientConGroupController.cs
+++ b/Robust.Client/Console/ClientConGroupController.cs
@@ -48,6 +48,13 @@ namespace Robust.Client.Console
return _clientConGroup.CanAdminPlace;
}
+ public bool CanScript()
+ {
+ if (_clientConGroup == null)
+ return false;
+ return _clientConGroup.CanScript;
+ }
+
///
/// Update client console group data with message from the server.
///
diff --git a/Robust.Client/Console/Commands/Scripting.cs b/Robust.Client/Console/Commands/Scripting.cs
index f0c5b1ba7..3ab552d59 100644
--- a/Robust.Client/Console/Commands/Scripting.cs
+++ b/Robust.Client/Console/Commands/Scripting.cs
@@ -1,8 +1,11 @@
-#if SCRIPTING
using Robust.Client.Interfaces.Console;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
+#if CLIENT_SCRIPTING
internal sealed class ScriptConsoleCommand : IConsoleCommand
{
public string Command => "csi";
@@ -11,10 +14,31 @@ namespace Robust.Client.Console.Commands
public bool Execute(IDebugConsole console, params string[] args)
{
- new ScriptConsole().OpenCenteredMinSize();
+ new ScriptConsoleClient().OpenCenteredMinSize();
+
+ return false;
+ }
+ }
+#endif
+
+ internal sealed class ServerScriptConsoleCommand : IConsoleCommand
+ {
+ public string Command => "scsi";
+ public string Description => "Opens a C# interactive console on the server.";
+ public string Help => "scsi";
+
+ public bool Execute(IDebugConsole console, params string[] args)
+ {
+ var mgr = IoCManager.Resolve();
+ if (!mgr.CanScript)
+ {
+ console.AddLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
+ return false;
+ }
+
+ mgr.StartSession();
return false;
}
}
}
-#endif
diff --git a/Robust.Client/Console/IClientConGroupController.cs b/Robust.Client/Console/IClientConGroupController.cs
index 29ce7f7fc..7aefcff84 100644
--- a/Robust.Client/Console/IClientConGroupController.cs
+++ b/Robust.Client/Console/IClientConGroupController.cs
@@ -13,6 +13,7 @@ namespace Robust.Client.Console
bool CanCommand(string cmdName);
bool CanViewVar();
bool CanAdminPlace();
+ bool CanScript();
event Action ConGroupUpdated;
}
}
diff --git a/Robust.Client/Console/IScriptClient.cs b/Robust.Client/Console/IScriptClient.cs
new file mode 100644
index 000000000..4c29f7d7b
--- /dev/null
+++ b/Robust.Client/Console/IScriptClient.cs
@@ -0,0 +1,13 @@
+namespace Robust.Client.Console
+{
+ ///
+ /// Client manager for server side scripting.
+ ///
+ public interface IScriptClient
+ {
+ void Initialize();
+
+ bool CanScript { get; }
+ void StartSession();
+ }
+}
diff --git a/Robust.Client/Console/ScriptClient.ScriptConsoleServer.cs b/Robust.Client/Console/ScriptClient.ScriptConsoleServer.cs
new file mode 100644
index 000000000..1bf296cee
--- /dev/null
+++ b/Robust.Client/Console/ScriptClient.ScriptConsoleServer.cs
@@ -0,0 +1,100 @@
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
+using Robust.Shared.Network.Messages;
+using Robust.Shared.Utility;
+
+namespace Robust.Client.Console
+{
+ public partial class ScriptClient
+ {
+ private sealed class ScriptConsoleServer : ScriptConsole
+ {
+ private readonly ScriptClient _client;
+ private readonly int _session;
+
+ private int _linesEntered;
+ private string _lastEnteredText;
+
+ public ScriptConsoleServer(ScriptClient client, int session)
+ {
+ _client = client;
+ _session = session;
+ Title = Loc.GetString("Robust C# Interactive (SERVER)");
+
+ OutputPanel.AddText(Loc.GetString(@"Robust C# interactive console (SERVER)."));
+ OutputPanel.AddText(">");
+ }
+
+ protected override void Run()
+ {
+ if (RunButton.Disabled || string.IsNullOrWhiteSpace(InputBar.Text))
+ {
+ return;
+ }
+
+ RunButton.Disabled = true;
+
+ var msg = _client._netManager.CreateNetMessage();
+ msg.ScriptSession = _session;
+ msg.Code = _lastEnteredText = InputBar.Text;
+
+ _client._netManager.ClientSendMessage(msg);
+
+ InputBar.Clear();
+
+
+ }
+
+ public override void Close()
+ {
+ base.Close();
+
+ _client.ConsoleClosed(_session);
+ }
+
+ public void ReceiveResponse(MsgScriptResponse response)
+ {
+ RunButton.Disabled = false;
+
+ // Remove > or . at the end of the output panel.
+ OutputPanel.RemoveEntry(^1);
+ _linesEntered += 1;
+
+ if (!response.WasComplete)
+ {
+ if (_linesEntered == 1)
+ {
+ OutputPanel.AddText($"> {_lastEnteredText}");
+ }
+ else
+ {
+ OutputPanel.AddText($". {_lastEnteredText}");
+ }
+
+ OutputPanel.AddText(".");
+ return;
+ }
+
+ // Remove echo of partial submission from the output panel.
+ for (var i = 1; i < _linesEntered; i++)
+ {
+ OutputPanel.RemoveEntry(^1);
+ }
+
+ _linesEntered = 0;
+
+ // Echo entered script.
+ var echoMessage = new FormattedMessage();
+ echoMessage.PushColor(Color.FromHex("#D4D4D4"));
+ echoMessage.AddText("> ");
+ echoMessage.AddMessage(response.Echo);
+ OutputPanel.AddMessage(echoMessage);
+
+ OutputPanel.AddMessage(response.Response);
+
+ OutputPanel.AddText(">");
+ }
+ }
+ }
+}
diff --git a/Robust.Client/Console/ScriptClient.cs b/Robust.Client/Console/ScriptClient.cs
new file mode 100644
index 000000000..ab35c9bf9
--- /dev/null
+++ b/Robust.Client/Console/ScriptClient.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using Robust.Shared.Interfaces.Network;
+using Robust.Shared.IoC;
+using Robust.Shared.Network.Messages;
+
+namespace Robust.Client.Console
+{
+ public partial class ScriptClient : IScriptClient
+ {
+ [Dependency] private readonly IClientConGroupController _conGroupController = default;
+ [Dependency] private readonly IClientNetManager _netManager = default;
+
+ private readonly Dictionary _activeConsoles = new Dictionary();
+
+ private int _nextSessionId = 1;
+
+ public void Initialize()
+ {
+ _netManager.RegisterNetMessage(MsgScriptStop.NAME);
+ _netManager.RegisterNetMessage(MsgScriptEval.NAME);
+ _netManager.RegisterNetMessage(MsgScriptStart.NAME);
+ _netManager.RegisterNetMessage(MsgScriptResponse.NAME, ReceiveScriptResponse);
+ _netManager.RegisterNetMessage(MsgScriptStartAck.NAME, ReceiveScriptStartAckResponse);
+ }
+
+ private void ReceiveScriptStartAckResponse(MsgScriptStartAck message)
+ {
+ var session = message.ScriptSession;
+
+ var console = new ScriptConsoleServer(this, session);
+ _activeConsoles.Add(session, console);
+ console.Open();
+ }
+
+ private void ReceiveScriptResponse(MsgScriptResponse message)
+ {
+ if (!_activeConsoles.TryGetValue(message.ScriptSession, out var console))
+ {
+ return;
+ }
+
+ console.ReceiveResponse(message);
+ }
+
+ public bool CanScript => _conGroupController.CanScript();
+
+ public void StartSession()
+ {
+ if (!CanScript)
+ {
+ throw new InvalidOperationException("We do not have scripting permission.");
+ }
+
+ var msg = _netManager.CreateNetMessage();
+ msg.ScriptSession = _nextSessionId++;
+ _netManager.ClientSendMessage(msg);
+ }
+
+ private void ConsoleClosed(int session)
+ {
+ _activeConsoles.Remove(session);
+
+ var msg = _netManager.CreateNetMessage();
+ msg.ScriptSession = session;
+ _netManager.ClientSendMessage(msg);
+ }
+ }
+}
diff --git a/Robust.Client/Console/ScriptConsole.cs b/Robust.Client/Console/ScriptConsole.cs
deleted file mode 100644
index 8f45ca8cf..000000000
--- a/Robust.Client/Console/ScriptConsole.cs
+++ /dev/null
@@ -1,366 +0,0 @@
-#if SCRIPTING
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using JetBrains.Annotations;
-using Lidgren.Network;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Classification;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Scripting;
-using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
-using Microsoft.CodeAnalysis.Scripting;
-using Microsoft.CodeAnalysis.Text;
-using Robust.Client.Graphics.Drawing;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.ViewVariables;
-using Robust.Shared.Interfaces.GameObjects;
-using Robust.Shared.Interfaces.Reflection;
-using Robust.Shared.IoC;
-using Robust.Shared.Localization;
-using Robust.Shared.Maths;
-using Robust.Shared.Utility;
-using YamlDotNet.RepresentationModel;
-
-#nullable enable
-
-namespace Robust.Client.Console
-{
- internal sealed class ScriptConsole : SS14Window
- {
-#pragma warning disable 649
- [Dependency] private readonly IReflectionManager _reflectionManager = default!;
-#pragma warning restore 649
-
- private static readonly CSharpParseOptions _parseOptions =
- new CSharpParseOptions(kind: SourceCodeKind.Script, languageVersion: LanguageVersion.Latest);
-
- private static readonly Func