Compare commits

...

43 Commits

Author SHA1 Message Date
metalgearsloth
18fcab6f71 Fix loadbp on paused maps (#1531)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-04 23:29:37 +11:00
Acruid
1ae6c32c62 Adds the net_entityreport console command, that displays a network history of entity updates. 2021-02-03 23:19:47 -08:00
Pieter-Jan Briers
cf6925f19b Makes sudo command show output to user. (#1530) 2021-02-03 19:31:32 -08:00
Pieter-Jan Briers
b4c7ffe38a Adds an exec command.
Just like Quake. Man that Carmack guy was onto something.
2021-02-04 03:40:03 +01:00
Pieter-Jan Briers
d5c2f45f14 Fix accidental usage of OpenGL DSA in screenshot code. 2021-02-03 21:10:48 +01:00
Pieter-Jan Briers
dcc88d2d36 Fix KHR_debug detection on OpenGL ES. 2021-02-03 21:10:48 +01:00
metalgearsloth
a274b8dfc2 EntityQuery no longer yields paused by default (#1521)
* Change EntityQuery to not get paused by default

* GetAllComponents

* Fix shell commands

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-04 00:20:31 +11:00
Paul Ritter
f42c1379e0 Revert "Viewport improvements. (#1510)" (#1527)
This reverts commit fc5e3ab69d.
2021-02-03 12:25:12 +01:00
Pieter-Jan Briers
c4fa7e98d4 Fix clients not disconnecting correctly when client is closed. 2021-02-03 00:04:41 +01:00
Vera Aguilera Puerto
8b9dadffb1 MIDI improvements (#1526) 2021-02-02 18:46:24 +01:00
Vera Aguilera Puerto
5e99c6d04d ViewVariables server-side "Add Component" button isn't hidden anymore when searching for components. 2021-02-02 13:28:56 +01:00
Vera Aguilera Puerto
606d232dbb Update ViewVariables to use the new commands API 2021-02-02 13:26:41 +01:00
Vera Aguilera Puerto
df877582a6 You can now add or remove components from the comfort of ViewVariables (#1524) 2021-02-02 12:01:54 +01:00
Acruid
8ffdb090e6 Adds a property on ITransformComponent that prevents the entity from being rotated. Resolves #1479. 2021-02-01 20:27:17 -08:00
Acruid
033d6ffb22 Fixes bug where effects were dying in transit from lag. Resolves #1256. 2021-02-01 19:17:34 -08:00
Acruid
3eb6e067f9 Console Unify (#1513)
* Renamed shared ICommand to IConsoleCommand.

* Lots of refactoring into a shared context.

* Removed ICommonSession from server concmd Execute.

* Added argStr parameter to concmd execute.

* The execute function of client concmds now returns void, use the new shell.RemoteExecuteCommand function to forward commands.

# Conflicts:
#	Robust.Client/Console/Commands/Debug.cs

* Finally move shells and commands into shared.

* Console commands can now be registered directly without a class in a shared context.

* Pulled up ConsoleHost and Console shell into a shared context.

* Pulled up half the functions of ConsoleHost into a shared context.

* Repair rebase damage.

* Make LoadConsoleCommands function not remove any previously registered commands.
2021-02-01 16:40:26 -08:00
Vera Aguilera Puerto
1f64f93ef4 Update nfluidsynth to latest version, 0.3.1 2021-02-01 21:33:22 +01:00
Pieter-Jan Briers
7dd617b0d6 Adds reflection helper methods to script globals. 2021-02-01 19:41:02 +01:00
metalgearsloth
0567b70704 Optimise showbb (#1519)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-02-01 20:15:50 +11:00
Pieter-Jan Briers
9963e89c14 Sanitize ResourcePath to disallow .. shenigans. 2021-01-31 22:38:40 +01:00
Remie Richards
29e0faed88 Can now animate ITransformComponent.WorldPosition (#1518) 2021-01-30 23:26:01 +01:00
Pieter-Jan Briers
0ce6ff9870 Fix tickrate sync if non-default tickrate saved to client config.
Fixes #1516

Thanks to blue on Discord for helping me diagnose this.
2021-01-30 01:27:42 +01:00
Vera Aguilera Puerto
dbe9a96dfa Reorganizes NumericsHelpers into different files using partial classes. (#1514) 2021-01-29 12:46:27 +01:00
Vera Aguilera Puerto
fc5e3ab69d Viewport improvements. (#1510)
* Remove the main viewport.

* Re-add main viewport in a different place

* Move screen/map translation into ViewportContainer

* Support for viewport resolution

* Fix postfx, add WorldToScreen to Viewport

* Remove useless cast

* Some cleanup.

* Fix incorrect worldBounds when rendering viewports

* nullability

Co-authored-by: 20kdc <asdd2808@gmail.com>
2021-01-29 12:45:04 +01:00
Julian Giebel
ed4974141c Add TryCastValue to CollectionExtensions.cs (#1515)
* Add TryCastValue to CollectionExtensions.cs

* Change TryCastValue to be more generic

* Add notnull constraing for TKey to TryCastValue

Co-authored-by: Julian Giebel <j.giebel@netrocks.info>
2021-01-28 20:45:11 +01:00
Pieter-Jan Briers
3e5efd5ed0 Add AnimatedTextureRect 2021-01-24 23:00:02 +01:00
Pieter-Jan Briers
e1110eadb4 Better unified handling of SpriteSpecifiers
Add more utilities, most notably IRsiStateLike, to make working with SpriteSpecifiers easier. Especially when animated.
2021-01-24 22:59:47 +01:00
Pieter-Jan Briers
06ace83a73 Allow audio playback without grids. 2021-01-24 16:04:23 +01:00
Pieter-Jan Briers
cd01ca924b Fix broken PanelContainer due to previous commit.
God I am tired.
2021-01-24 04:20:51 +01:00
Pieter-Jan Briers
38ad8ce132 Fix UI scaling weirdness with PanelContainer. 2021-01-23 21:51:35 +01:00
20kdc
ee440c2df9 Make the lighting manager much more configurable, including a console lock (#1506) 2021-01-23 21:17:49 +01:00
Pieter-Jan Briers
32f3c863fb Fix UI scaling bugs with ProgressBar 2021-01-23 20:15:45 +01:00
Pieter-Jan Briers
b00e0bef5a Fix UI scaling bug with RichTextLabel 2021-01-23 16:02:31 +01:00
Pieter-Jan Briers
0a09b27918 Fix UI scaling bug with GridContainer 2021-01-23 16:02:23 +01:00
Paul
c9f6a4e32a fixes enum VV 2021-01-21 15:33:42 +01:00
Pieter-Jan Briers
b205a14f69 Exception tolerance for NetManager.OnDisconnect 2021-01-20 21:07:02 +01:00
Pieter-Jan Briers
d5f3292e0a Unregister OnSessionOnPlayerStatusChanged on bound user interfaces.
I am frankly flabbergasted this is only a problem now.
2021-01-20 21:00:24 +01:00
Pieter-Jan Briers
561e4b330e Fix ALL components memory leaking.
:irrational:
2021-01-20 20:45:02 +01:00
Paul
36a5d102ff prevent one error from killing the entire namegenerator from running 2021-01-17 17:17:02 +01:00
Pieter-Jan Briers
b9c39e0953 Fix reconnecting. 2021-01-17 16:08:48 +01:00
Pieter-Jan Briers
ad4c8be132 Add system for preserving Map UIDs across edits. 2021-01-17 15:51:32 +01:00
kira-er
988cbf9a87 VV Enum (#1503) 2021-01-17 01:50:26 +01:00
Acruid
e26512001a Completely removed MsgSetTickRate, the NetConfigurationManager replaces the functionality. This builds on top of the previous commit.
Fixes bug where server was not sending the entire set of replicated cvars.
2021-01-16 15:33:44 -08:00
141 changed files with 4924 additions and 3557 deletions

View File

@@ -153,7 +153,7 @@ namespace {nameSpace}
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
return;
continue;
}
var txt = relevantXamlFile.GetText()?.ToString();
@@ -169,7 +169,7 @@ namespace {nameSpace}
DiagnosticSeverity.Error,
true),
Location.Create(xamlFileName, new TextSpan(0,0), new LinePositionSpan(new LinePosition(0,0),new LinePosition(0,0)))));
return;
continue;
}
try
@@ -189,7 +189,7 @@ namespace {nameSpace}
DiagnosticSeverity.Error,
true),
typeSymbol.Locations[0]));
return;
continue;
}
}
}

View File

@@ -72,9 +72,9 @@ namespace Robust.Client.Audio.Midi
int PlayerTotalTick { get; }
/// <summary>
/// Gets the current tick of the MIDI player.
/// Gets or sets (seeks) the current tick of the MIDI player.
/// </summary>
int PlayerTick { get; }
int PlayerTick { get; set; }
/// <summary>
/// Gets the current tick of the sequencer.
@@ -235,7 +235,16 @@ namespace Robust.Client.Audio.Midi
public bool DisableProgramChangeEvent { get; set; } = true;
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
public int PlayerTick => _player?.CurrentTick ?? 0;
public int PlayerTick
{
get => _player?.CurrentTick ?? 0;
set
{
lock (_playerStateLock)
_player?.Seek(Math.Max(Math.Min(value, PlayerTotalTick), 0));
}
}
public uint SequencerTick => _sequencer?.Tick ?? 0;
public double SequencerTimeScale => _sequencer?.TimeScale ?? 0;
@@ -310,9 +319,8 @@ namespace Robust.Client.Audio.Midi
lock (_playerStateLock)
{
if (_player == null)
_player = new NFluidsynth.Player(_synth);
_player.Stop();
_player?.Dispose();
_player = new NFluidsynth.Player(_synth);
_player.AddMem(buffer);
_player.SetPlaybackCallback(MidiPlayerEventHandler);
_player.Play();
@@ -351,10 +359,7 @@ namespace Robust.Client.Audio.Midi
public void StopAllNotes()
{
for (var i = 0; i < 16; i++)
{
_synth.AllNotesOff(i);
}
_synth.AllNotesOff(-1);
}
public void LoadSoundfont(string filename, bool resetPresets = false)
@@ -475,12 +480,6 @@ namespace Robust.Client.Audio.Midi
lock(_playerStateLock)
switch (midiEvent.Type)
{
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
case 1:
case 5:
case 81:
break;
// Note On 0x80
case 144:
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
@@ -503,8 +502,10 @@ namespace Robust.Client.Audio.Midi
// Program Change - 0xC0
case 192:
if(!DisableProgramChangeEvent)
if (!DisableProgramChangeEvent)
_synth.ProgramChange(midiEvent.Channel, midiEvent.Program);
else
return;
break;
// Channel Pressure - 0xD0
@@ -517,13 +518,17 @@ namespace Robust.Client.Audio.Midi
_synth.PitchBend(midiEvent.Channel, midiEvent.Pitch);
break;
// Sometimes MIDI files spam these for no good reason and I can't find any info on what they are.
case 1:
case 5:
case 81:
// System Messages - 0xF0
case 240:
break;
return;
default:
_midiSawmill.Warning("Unhandled midi event of type {0}", midiEvent.Type, midiEvent);
break;
return;
}
}
catch (FluidSynthInteropException)

View File

@@ -15,7 +15,6 @@ using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Utility;
namespace Robust.Client
@@ -50,12 +49,11 @@ namespace Robust.Client
/// <inheritdoc />
public void Initialize()
{
_net.RegisterNetMessage<MsgSetTickRate>(MsgSetTickRate.NAME, HandleSetTickRate);
_net.Connected += OnConnected;
_net.ConnectFailed += OnConnectFailed;
_net.Disconnect += OnNetDisconnect;
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged);
_configManager.OnValueChanged(CVars.NetTickrate, TickRateChanged, invokeImmediately: true);
_playMan.Initialize();
_debugDrawMan.Initialize();
@@ -70,7 +68,7 @@ namespace Robust.Client
}
_timing.TickRate = (byte) tickrate;
Logger.InfoS("client", $"Tickrate changed to: {tickrate}");
Logger.InfoS("client", $"Tickrate changed to: {tickrate} on tick {_timing.CurTick}");
}
/// <inheritdoc />
@@ -176,6 +174,7 @@ namespace Robust.Client
private void Reset()
{
_configManager.ClearReceivedInitialNwVars();
OnRunLevelChanged(ClientRunLevel.Initialize);
}
@@ -202,12 +201,6 @@ namespace Robust.Client
Reset();
}
private void HandleSetTickRate(MsgSetTickRate message)
{
_timing.TickRate = message.NewTickRate;
Logger.InfoS("client", $"Tickrate changed to: {message.NewTickRate} on tick {_timing.CurTick}");
}
private void OnLocalStatusChanged(object? obj, StatusEventArgs eventArgs)
{
// player finished fully connecting to the server.

View File

@@ -78,7 +78,7 @@ namespace Robust.Client
IoCManager.Register<IDebugDrawingManager, DebugDrawingManager>();
IoCManager.Register<ILightManager, LightManager>();
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
IoCManager.Register<IClientConsole, ClientConsole>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
IoCManager.Register<IFontManager, FontManager>();
IoCManager.Register<IFontManagerInternal, FontManager>();
IoCManager.Register<IMidiManager, MidiManager>();

View File

@@ -1,243 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
using Robust.Client.Log;
using Robust.Client.Utility;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public string Text { get; }
public Color Color { get; }
public AddStringArgs(string text, Color color)
{
Text = text;
Color = color;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
internal sealed class ClientConsole : IClientConsole, IDebugConsole
{
private static readonly Color MsgColor = new Color(65, 105, 225);
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly ILogManager logManager = default!;
private readonly Dictionary<string, IConsoleCommand> _commands = new Dictionary<string, IConsoleCommand>();
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
_network.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
_network.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
_network.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
Reset();
logManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
/// <inheritdoc />
public void Reset()
{
_commands.Clear();
_requestedCommands = false;
_network.Connected += OnNetworkConnected;
InitializeCommands();
SendServerCommandRequest();
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _commands;
public void AddLine(string text, Color color)
{
AddString?.Invoke(this, new AddStringArgs(text, color));
}
public void AddLine(string text)
{
AddLine(text, Color.White);
}
public void Clear()
{
ClearText?.Invoke(this, EventArgs.Empty);
}
public event EventHandler<AddStringArgs>? AddString;
public event EventHandler? ClearText;
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
private void HandleConCmdAck(MsgConCmdAck msg)
{
AddLine("< " + msg.Text, MsgColor);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;
// Do not do duplicate commands.
if (_commands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
_commands[commandName] = command;
}
}
/// <summary>
/// Processes commands (chat messages starting with /)
/// </summary>
/// <param name="text">input text</param>
public void ProcessCommand(string text)
{
if (string.IsNullOrWhiteSpace(text))
return;
// echo the command locally
AddLine("> " + text, Color.Lime);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(text, args);
var commandname = args[0];
var forward = true;
if (_commands.ContainsKey(commandname))
{
var command = _commands[commandname];
args.RemoveAt(0);
forward = command.Execute(this, args.ToArray());
}
else if (!_network.IsConnected)
{
AddLine("Unknown command: " + commandname, Color.Red);
return;
}
if (forward)
SendServerConsoleCommand(text);
}
/// <summary>
/// Locates and registeres all local commands.
/// </summary>
private void InitializeCommands()
{
foreach (var t in _reflectionManager.GetAllChildren<IConsoleCommand>())
{
var instance = (IConsoleCommand)Activator.CreateInstance(t)!;
if (_commands.ContainsKey(instance.Command))
throw new InvalidOperationException($"Command already registered: {instance.Command}");
_commands[instance.Command] = instance;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
public void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmdReg>();
_network.ClientSendMessage(msg);
_requestedCommands = true;
}
/// <summary>
/// Sends a command directly to the server.
/// </summary>
private void SendServerConsoleCommand(string text)
{
if (_network == null || !_network.IsConnected)
return;
var msg = _network.CreateNetMessage<MsgConCmd>();
msg.Text = text;
_network.ClientSendMessage(msg);
}
public void AddFormattedLine(FormattedMessage message)
{
// Why the hell does this class implement IDebugConsole.
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
public string Command { get; }
public string Help { get; }
public string Description { get; }
// Always forward to server.
public bool Execute(IDebugConsole console, params string[] args)
{
return true;
}
}
}

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using Robust.Client.Log;
using Robust.Shared.Console;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public class AddStringArgs : EventArgs
{
public Color Color { get; }
public string Text { get; }
public AddStringArgs(string text, Color color)
{
Text = text;
Color = color;
}
}
public class AddFormattedMessageArgs : EventArgs
{
public readonly FormattedMessage Message;
public AddFormattedMessageArgs(FormattedMessage message)
{
Message = message;
}
}
/// <inheritdoc cref="IClientConsoleHost" />
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
{
private static readonly Color _msgColor = new(65, 105, 225);
private bool _requestedCommands;
/// <inheritdoc />
public void Initialize()
{
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME);
Reset();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
/// <inheritdoc />
public void Reset()
{
AvailableCommands.Clear();
_requestedCommands = false;
NetManager.Connected += OnNetworkConnected;
LoadConsoleCommands();
SendServerCommandRequest();
}
public event EventHandler<AddStringArgs>? AddString;
public event EventHandler<AddFormattedMessageArgs>? AddFormatted;
public void AddFormattedLine(FormattedMessage message)
{
AddFormatted?.Invoke(this, new AddFormattedMessageArgs(message));
}
public override void WriteLine(ICommonSession? session, string text, Color color)
{
AddString?.Invoke(this, new AddStringArgs(text, color));
}
public override void ExecuteCommand(ICommonSession? session, string command)
{
if (string.IsNullOrWhiteSpace(command))
return;
// echo the command locally
WriteLine(null, "> " + command, Color.Lime);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
var commandname = args[0];
if (AvailableCommands.ContainsKey(commandname))
{
var command1 = AvailableCommands[commandname];
args.RemoveAt(0);
command1.Execute(new ConsoleShell(this, null), command, args.ToArray());
}
else if (!NetManager.IsConnected) WriteLine(null, "Unknown command: " + commandname, Color.Red);
}
/// <summary>
/// Sends a command directly to the server.
/// </summary>
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
if (!NetManager.IsConnected)
return;
var msg = NetManager.CreateNetMessage<MsgConCmd>();
msg.Text = command;
NetManager.ClientSendMessage(msg);
}
public override void WriteLine(ICommonSession? session, string text)
{
WriteLine(null, text, Color.White);
}
/// <inheritdoc />
public void Dispose()
{
// We don't have anything to dispose.
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)
{
SendServerCommandRequest();
}
private void HandleConCmdAck(MsgConCmdAck msg)
{
WriteLine(null, "< " + msg.Text, _msgColor);
}
private void HandleConCmdReg(MsgConCmdReg msg)
{
foreach (var cmd in msg.Commands)
{
var commandName = cmd.Name;
// Do not do duplicate commands.
if (AvailableCommands.ContainsKey(commandName))
{
Logger.DebugS("console", $"Server sent console command {commandName}, but we already have one with the same name. Ignoring.");
continue;
}
var command = new ServerDummyCommand(commandName, cmd.Help, cmd.Description);
AvailableCommands[commandName] = command;
}
}
/// <summary>
/// Requests remote commands from server.
/// </summary>
private void SendServerCommandRequest()
{
if (_requestedCommands)
return;
if (!NetManager.IsConnected)
return;
var msg = NetManager.CreateNetMessage<MsgConCmdReg>();
NetManager.ClientSendMessage(msg);
_requestedCommands = true;
}
}
/// <summary>
/// These dummies are made purely so list and help can list server-side commands.
/// </summary>
[Reflect(false)]
internal class ServerDummyCommand : IConsoleCommand
{
public string Command { get; }
public string Description { get; }
public string Help { get; }
internal ServerDummyCommand(string command, string help, string description)
{
Command = command;
Help = help;
Description = description;
}
// Always forward to server.
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
shell.RemoteExecuteCommand(argStr);
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -14,13 +14,13 @@ namespace Robust.Client.Console.Commands
public string Description => "Adds a component to an entity on the client";
public string Help => "addcompc <uid> <componentName>";
public bool Execute(IDebugConsole shell, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.AddLine("Wrong number of arguments");
return false;
shell.WriteLine("Wrong number of arguments");
return;
}
var entityUid = EntityUid.Parse(args[0]);
@@ -36,8 +36,6 @@ namespace Robust.Client.Console.Commands
component.Owner = entity;
compManager.AddComponent(entity, component);
return false;
}
}
@@ -48,12 +46,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Removes a component from an entity.";
public string Help => "rmcompc <uid> <componentName>";
public bool Execute(IDebugConsole shell, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.AddLine("Wrong number of arguments");
return false;
shell.WriteLine("Wrong number of arguments");
return;
}
var entityUid = EntityUid.Parse(args[0]);
@@ -65,8 +63,6 @@ namespace Robust.Client.Console.Commands
var registration = compFactory.GetRegistration(componentName);
compManager.RemoveComponent(entityUid, registration.Type);
return false;
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -13,18 +13,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Spawns a client-side entity with specific type at your feet.";
public string Help => "cspawn <entity type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
if (player?.ControlledEntity == null)
{
console.AddLine("You don't have an attached entity.");
return false;
shell.WriteLine("You don't have an attached entity.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
entityManager.SpawnEntity(args[0], player.ControlledEntity.Transform.Coordinates);
return false;
}
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
@@ -12,12 +12,12 @@ namespace Robust.Client.Console.Commands
[UsedImplicitly]
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
{
public bool Execute(IDebugConsole console, params string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
console.AddLine("Must provide exactly one or two arguments.", Color.Red);
return false;
shell.WriteLine("Must provide exactly one or two arguments.", Color.Red);
return;
}
var configManager = IoCManager.Resolve<IConfigurationManager>();
@@ -26,21 +26,21 @@ namespace Robust.Client.Console.Commands
if (name == "?")
{
var cvars = configManager.GetRegisteredCVars().OrderBy(c => c);
console.AddLine(string.Join("\n", cvars));
return false;
shell.WriteLine(string.Join("\n", cvars));
return;
}
if (!configManager.IsCVarRegistered(name))
{
console.AddLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
return false;
shell.WriteLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.", Color.Red);
return;
}
if (args.Length == 1)
{
// Read CVar
var value = configManager.GetCVar<object>(name);
console.AddLine(value.ToString() ?? "");
shell.WriteLine(value.ToString() ?? "");
}
else
{
@@ -54,11 +54,9 @@ namespace Robust.Client.Console.Commands
}
catch (FormatException)
{
console.AddLine($"Input value is in incorrect format for type {type}");
shell.WriteLine($"Input value is in incorrect format for type {type}");
}
}
return false;
}
}
@@ -69,10 +67,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Saves the client configuration to the config file";
public string Help => "saveconfig";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
return false;
}
}

View File

@@ -1,9 +1,8 @@
// This file is for commands that do something to the console itself.
// This file is for commands that do something to the console itself.
// Not some generic console command type.
// Couldn't think of a better name sorry.
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -17,10 +16,9 @@ namespace Robust.Client.Console.Commands
public string Help => "Clears the debug console of all messages.";
public string Description => "Clears the console.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.Clear();
return false;
shell.Clear();
}
}
@@ -30,15 +28,14 @@ namespace Robust.Client.Console.Commands
public string Help => "Fills the console with some nonsense for debugging.";
public string Description => "Fill up the console for debugging.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Color[] colors = { Color.Green, Color.Blue, Color.Red };
var random = IoCManager.Resolve<IRobustRandom>();
for (int x = 0; x < 50; x++)
{
console.AddLine("filling...", colors[random.Next(0, colors.Length)]);
shell.WriteLine("filling...", colors[random.Next(0, colors.Length)]);
}
return false;
}
}
}

View File

@@ -12,7 +12,6 @@ using System.Text.RegularExpressions;
using Robust.Client.Input;
using System.Threading;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Debugging;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -26,6 +25,7 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Asynchronous;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
@@ -49,16 +49,14 @@ namespace Robust.Client.Console.Commands
public string Help => "Dump entity list";
public string Description => "Dumps entity list of UIDs and prototype.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
foreach (var e in entityManager.GetEntities().OrderBy(e => e.Uid))
{
console.AddLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
shell.WriteLine($"entity {e.Uid}, {e.Prototype?.ID}, {e.Transform.Coordinates}.", Color.White);
}
return false;
}
}
@@ -68,12 +66,12 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: getcomponentregistration <componentName>";
public string Description => "Gets component registration information";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var componentFactory = IoCManager.Resolve<IComponentFactory>();
@@ -94,19 +92,17 @@ namespace Robust.Client.Console.Commands
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
console.AddLine(message.ToString(), Color.White);
shell.WriteLine(message.ToString(), Color.White);
foreach (var type in registration.References)
{
console.AddLine($" {type}", Color.White);
shell.WriteLine($" {type}", Color.White);
}
}
catch (UnknownComponentException)
{
console.AddLine($"No registration found for '{args[0]}'", Color.Red);
shell.WriteLine($"No registration found for '{args[0]}'", Color.Red);
}
return false;
}
}
@@ -119,14 +115,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles a debug monitor in the F3 menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var monitor = IoCManager.Resolve<IUserInterfaceManager>().DebugMonitors;
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
switch (args[0])
@@ -159,11 +155,9 @@ namespace Robust.Client.Console.Commands
monitor.ShowInput ^= true;
break;
default:
console.AddLine($"Invalid key: {args[0]}");
shell.WriteLine($"Invalid key: {args[0]}");
break;
}
return false;
}
}
@@ -173,7 +167,7 @@ namespace Robust.Client.Console.Commands
public string Help => "Throws an exception";
public string Description => "Throws an exception";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
throw new InvalidOperationException("Fuck");
}
@@ -185,11 +179,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all bounding boxes in the game, showing their size.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugColliders = !mgr.DebugColliders;
return false;
}
}
@@ -199,11 +192,10 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Enables debug drawing over all entity positions in the game.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IDebugDrawing>();
mgr.DebugPositions = !mgr.DebugPositions;
return false;
}
}
@@ -213,25 +205,24 @@ namespace Robust.Client.Console.Commands
public string Help => "Usage: showrays <raylifetime>";
public string Description => "Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
return;
}
var mgr = IoCManager.Resolve<IDebugDrawingManager>();
mgr.DebugDrawRays = !mgr.DebugDrawRays;
console.AddLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
shell.WriteLine("Toggled showing rays to:" + mgr.DebugDrawRays.ToString(), Color.Green);
mgr.DebugRayLifetime = TimeSpan.FromSeconds((double)int.Parse(args[0], CultureInfo.InvariantCulture));
return false;
}
}
@@ -241,10 +232,9 @@ namespace Robust.Client.Console.Commands
public string Help => "";
public string Description => "Immediately disconnect from the server and go back to the main menu.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IClientNetManager>().ClientDisconnect("Disconnect command used.");
return false;
}
}
@@ -257,33 +247,33 @@ namespace Robust.Client.Console.Commands
public string Description => "Displays verbose diagnostics for an entity.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if ((!new Regex(@"^c?[0-9]+$").IsMatch(args[0])))
{
console.AddLine("Malformed UID", Color.Red);
return false;
shell.WriteLine("Malformed UID", Color.Red);
return;
}
var uid = EntityUid.Parse(args[0]);
var entmgr = IoCManager.Resolve<IEntityManager>();
if (!entmgr.TryGetEntity(uid, out var entity))
{
console.AddLine("That entity does not exist. Sorry lad.", Color.Red);
return false;
shell.WriteLine("That entity does not exist. Sorry lad.", Color.Red);
return;
}
console.AddLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
console.AddLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
shell.WriteLine($"{entity.Uid}: {entity.Prototype?.ID}/{entity.Name}");
shell.WriteLine($"init/del/lmt: {entity.Initialized}/{entity.Deleted}/{entity.LastModifiedTick}");
foreach (var component in entity.GetAllComponents())
{
console.AddLine(component.ToString() ?? "");
shell.WriteLine(component.ToString() ?? "");
if (component is IComponentDebug debug)
{
foreach (var line in debug.GetDebugString().Split('\n'))
@@ -293,12 +283,10 @@ namespace Robust.Client.Console.Commands
continue;
}
console.AddLine("\t" + line);
shell.WriteLine("\t" + line);
}
}
}
return false;
}
}
@@ -308,12 +296,12 @@ namespace Robust.Client.Console.Commands
public string Help => "sggcell <gridID> <vector2i> [offset]\nThat vector2i param is in the form x<int>,y<int>.";
public string Description => "Lists entities on a snap grid cell.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2 && args.Length != 3)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
string gridId = args[0];
@@ -322,14 +310,14 @@ namespace Robust.Client.Console.Commands
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.",Color.Red);
return false;
shell.WriteLine($"{args[0]} is not a valid integer.",Color.Red);
return;
}
if (!new Regex(@"^-?[0-9]+,-?[0-9]+$").IsMatch(indices))
{
console.AddLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
return false;
shell.WriteLine("mapIndicies must be of form x<int>,y<int>", Color.Red);
return;
}
SnapGridOffset selectedOffset;
@@ -339,8 +327,8 @@ namespace Robust.Client.Console.Commands
}
else
{
console.AddLine("given offset type is not defined", Color.Red);
return false;
shell.WriteLine("given offset type is not defined", Color.Red);
return;
}
var mapMan = IoCManager.Resolve<IMapManager>();
@@ -354,16 +342,13 @@ namespace Robust.Client.Console.Commands
int.Parse(indices.Split(',')[1], CultureInfo.InvariantCulture)),
selectedOffset))
{
console.AddLine(entity.Owner.Uid.ToString());
shell.WriteLine(entity.Owner.Uid.ToString());
}
}
else
{
console.AddLine("grid does not exist", Color.Red);
return false;
shell.WriteLine("grid does not exist", Color.Red);
}
return false;
}
}
@@ -373,19 +358,17 @@ namespace Robust.Client.Console.Commands
public string Description => "Changes the name used when attempting to connect to the server.";
public string Help => Command + " <name>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var client = IoCManager.Resolve<IBaseClient>();
client.PlayerNameOverride = args[0];
console.AddLine($"Overriding player name to \"{args[0]}\".", Color.White);
return false;
shell.WriteLine($"Overriding player name to \"{args[0]}\".", Color.White);
}
}
@@ -395,12 +378,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Pre-caches a resource.";
public string Help => "ldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -412,8 +395,8 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteLine("Unable to find type", Color.Red);
return;
}
var getResourceMethod =
@@ -423,7 +406,6 @@ namespace Robust.Client.Console.Commands
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0], true });
return false;
}
}
@@ -433,12 +415,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Reloads a resource.";
public string Help => "rldrsc <path> <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var resourceCache = IoCManager.Resolve<IResourceCache>();
var reflection = IoCManager.Resolve<IReflectionManager>();
@@ -450,15 +432,14 @@ namespace Robust.Client.Console.Commands
}
catch(ArgumentException)
{
console.AddLine("Unable to find type", Color.Red);
return false;
shell.WriteLine("Unable to find type", Color.Red);
return;
}
var getResourceMethod = resourceCache.GetType().GetMethod("ReloadResource", new[] { typeof(string) });
DebugTools.Assert(getResourceMethod != null);
var generic = getResourceMethod!.MakeGenericMethod(type);
generic.Invoke(resourceCache, new object[] { args[0] });
return false;
}
}
@@ -468,18 +449,18 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the tile count of a grid";
public string Help => "Usage: gridtc <gridId>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
if (!int.TryParse(args[0], out var id))
{
console.AddLine($"{args[0]} is not a valid integer.");
return false;
shell.WriteLine($"{args[0]} is not a valid integer.");
return;
}
var gridId = new GridId(int.Parse(args[0]));
@@ -487,13 +468,11 @@ namespace Robust.Client.Console.Commands
if (mapManager.TryGetGrid(gridId, out var grid))
{
console.AddLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
return false;
shell.WriteLine(mapManager.GetGrid(gridId).GetAllTiles().Count().ToString());
}
else
{
console.AddLine($"No grid exists with id {id}",Color.Red);
return false;
shell.WriteLine($"No grid exists with id {id}",Color.Red);
}
}
}
@@ -504,7 +483,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Dump GUI tree to /guidump.txt in user data.";
public string Help => "guidump";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var root = IoCManager.Resolve<IUserInterfaceManager>().RootControl;
var res = IoCManager.Resolve<IResourceManager>();
@@ -514,8 +493,6 @@ namespace Robust.Client.Console.Commands
{
_writeNode(root, 0, writer);
}
return false;
}
private static void _writeNode(Control control, int indents, TextWriter writer)
@@ -575,7 +552,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Open a dummy UI testing window";
public string Help => "uitest";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var window = new SS14Window { CustomMinimumSize = (500, 400)};
var tabContainer = new TabContainer();
@@ -667,8 +644,6 @@ namespace Robust.Client.Console.Commands
});
window.OpenCentered();
return false;
}
}
@@ -678,11 +653,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Sets the system clipboard";
public string Help => "setclipboard <text>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
mgr.SetText(args[0]);
return false;
}
}
@@ -692,11 +666,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets the system clipboard";
public string Help => "getclipboard";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IClipboardManager>();
console.AddLine(mgr.GetText());
return false;
shell.WriteLine(mgr.GetText());
}
}
@@ -706,11 +679,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles light rendering.";
public string Help => "togglelight";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.Enabled = !mgr.Enabled;
return false;
if (!mgr.LockConsoleAccess)
mgr.Enabled = !mgr.Enabled;
}
}
@@ -720,12 +693,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles fov for client.";
public string Help => "togglefov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IEyeManager>();
if (mgr.CurrentEye != null)
mgr.CurrentEye.DrawFov = !mgr.CurrentEye.DrawFov;
return false;
}
}
@@ -735,11 +707,11 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles hard fov for client (for debugging space-station-14#2353).";
public string Help => "togglehardfov";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.DrawHardFov = !mgr.DrawHardFov;
return false;
if (!mgr.LockConsoleAccess)
mgr.DrawHardFov = !mgr.DrawHardFov;
}
}
@@ -749,11 +721,24 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggles shadow rendering.";
public string Help => "toggleshadows";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
mgr.DrawShadows = !mgr.DrawShadows;
return false;
if (!mgr.LockConsoleAccess)
mgr.DrawShadows = !mgr.DrawShadows;
}
}
internal class ToggleLightBuf : IConsoleCommand
{
public string Command => "togglelightbuf";
public string Description => "Toggles lighting rendering. This includes shadows but not FOV.";
public string Help => "togglelightbuf";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<ILightManager>();
if (!mgr.LockConsoleAccess)
mgr.DrawLighting = !mgr.DrawLighting;
}
}
@@ -763,7 +748,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Run the GC.";
public string Help => "gc [generation]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
@@ -774,10 +759,8 @@ namespace Robust.Client.Console.Commands
if (int.TryParse(args[0], out int result))
GC.Collect(result);
else
console.AddLine("Failed to parse argument.",Color.Red);
shell.WriteLine("Failed to parse argument.",Color.Red);
}
return false;
}
}
@@ -787,12 +770,10 @@ namespace Robust.Client.Console.Commands
public string Description => "Run the GC, fully, compacting LOH and everything.";
public string Help => "gcf";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(2, GCCollectionMode.Forced, true, true);
return false;
}
}
@@ -805,16 +786,16 @@ namespace Robust.Client.Console.Commands
public string Help => "gc_mode\nSee current GC Latencymode\ngc_mode [type]\n Change GC Latency mode to [type]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var prevMode = GCSettings.LatencyMode;
if (args.Length == 0)
{
console.AddLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
console.AddLine("possible modes:");
shell.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
shell.WriteLine("possible modes:");
foreach (int mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
{
console.AddLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
shell.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
}
}
else
@@ -826,16 +807,14 @@ namespace Robust.Client.Console.Commands
}
else if (!Enum.TryParse(args[0], true, out mode))
{
console.AddLine($"unknown gc latency mode: {args[0]}");
return false;
shell.WriteLine($"unknown gc latency mode: {args[0]}");
return;
}
console.AddLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
shell.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
GCSettings.LatencyMode = mode;
console.AddLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
shell.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
}
return false;
}
}
@@ -849,15 +828,13 @@ namespace Robust.Client.Console.Commands
public string Help => "szr_stats";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
console.AddLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.AddLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.AddLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
return false;
shell.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
shell.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
shell.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
}
}
@@ -868,7 +845,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Gets info about a chunk under your mouse cursor.";
public string Help => Command;
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapMan = IoCManager.Resolve<IMapManager>();
var inputMan = IoCManager.Resolve<IInputManager>();
@@ -878,8 +855,8 @@ namespace Robust.Client.Console.Commands
if (!mapMan.TryFindGridAt(mousePos, out var grid))
{
console.AddLine("No grid under your mouse cursor.");
return false;
shell.WriteLine("No grid under your mouse cursor.");
return;
}
var internalGrid = (IMapGridInternal)grid;
@@ -887,8 +864,7 @@ namespace Robust.Client.Console.Commands
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
var chunk = internalGrid.GetChunk(chunkIndex);
console.AddLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
return false;
shell.WriteLine($"worldBounds: {chunk.CalcWorldBounds()} localBounds: {chunk.CalcLocalBounds()}");
}
}
@@ -905,7 +881,7 @@ namespace Robust.Client.Console.Commands
public static ConcurrentDictionary<string, bool>? _reloadShadersQueued = new();
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IResourceCacheInternal resC;
if (args.Length == 1)
@@ -914,8 +890,8 @@ namespace Robust.Client.Console.Commands
{
if (_watchers != null)
{
console.AddLine("Already watching.");
return false;
shell.WriteLine("Already watching.");
return;
}
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -984,11 +960,11 @@ namespace Robust.Client.Console.Commands
{
IoCManager.Resolve<IResourceCache>()
.ReloadResource<ShaderSourceResource>(resPath);
console.AddLine($"Reloaded shader: {resPath}");
shell.WriteLine($"Reloaded shader: {resPath}");
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {resPath}");
shell.WriteLine($"Failed to reload shader: {resPath}");
}
_reloadShadersQueued.TryRemove(ev.FullPath, out var _);
@@ -1008,17 +984,17 @@ namespace Robust.Client.Console.Commands
++created;
}
console.AddLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
shell.WriteLine($"Created {created} shader directory watchers for {shaderCount} shaders.");
return false;
return;
}
if (args[0] == "-watch")
{
if (_watchers == null)
{
console.AddLine("No shader directory watchers active.");
return false;
shell.WriteLine("No shader directory watchers active.");
return;
}
var disposed = 0;
@@ -1030,19 +1006,19 @@ namespace Robust.Client.Console.Commands
_watchers = null;
console.AddLine($"Disposed of {disposed} shader directory watchers.");
shell.WriteLine($"Disposed of {disposed} shader directory watchers.");
return false;
return;
}
}
if (args.Length > 1)
{
console.AddLine("Not implemented.");
return false;
shell.WriteLine("Not implemented.");
return;
}
console.AddLine("Reloading content shader resources...");
shell.WriteLine("Reloading content shader resources...");
resC = IoCManager.Resolve<IResourceCacheInternal>();
@@ -1054,13 +1030,11 @@ namespace Robust.Client.Console.Commands
}
catch (Exception)
{
console.AddLine($"Failed to reload shader: {path}");
shell.WriteLine($"Failed to reload shader: {path}");
}
}
console.AddLine("Done.");
return false;
shell.WriteLine("Done.");
}
}
@@ -1071,14 +1045,14 @@ namespace Robust.Client.Console.Commands
public string Description => "Toggle fov and light debug layers";
public string Help => "cldbglyr <layer>: Toggle <layer>\ncldbglyr: Turn all Layers off";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var clyde = IoCManager.Resolve<IClydeInternal>();
if (args.Length < 1)
{
clyde.DebugLayers = ClydeDebugLayers.None;
return false;
return;
}
clyde.DebugLayers = args[0] switch
@@ -1087,8 +1061,6 @@ namespace Robust.Client.Console.Commands
"light" => ClydeDebugLayers.Light,
_ => ClydeDebugLayers.None
};
return false;
}
}
@@ -1098,12 +1070,12 @@ namespace Robust.Client.Console.Commands
public string Description => "Keys key info for a key";
public string Help => "keyinfo <Key>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine(Help);
return false;
shell.WriteLine(Help);
return;
}
var clyde = IoCManager.Resolve<IClydeInternal>();
@@ -1116,15 +1088,13 @@ namespace Robust.Client.Console.Commands
var scanCode = clyde.GetKeyScanCode(key);
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
shell.WriteLine($"name: '{name}' scan code: '{scanCode}' name via scan code: '{nameScanCode}'");
}
else if (int.TryParse(args[0], out var scanCode))
{
var nameScanCode = clyde.GetKeyNameScanCode(scanCode);
console.AddLine($"name via scan code: '{nameScanCode}'");
shell.WriteLine($"name via scan code: '{nameScanCode}'");
}
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.Maths;
@@ -12,23 +12,21 @@ namespace Robust.Client.Console.Commands
public string Description => "Dumps a type's members in a format suitable for the sandbox configuration file.";
public string Help => "Usage: dmetamem <type>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var type = Type.GetType(args[0]);
if (type == null)
{
console.AddLine("That type does not exist", Color.Red);
return false;
shell.WriteLine("That type does not exist", Color.Red);
return;
}
foreach (var sig in AssemblyTypeChecker.DumpMetaMembers(type))
{
System.Console.WriteLine(@$"- ""{sig}""");
console.AddLine(sig);
shell.WriteLine(sig);
}
return false;
}
}
#endif

View File

@@ -1,5 +1,5 @@
using System.Linq;
using Robust.Client.Interfaces.Console;
using System.Linq;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
@@ -12,37 +12,36 @@ namespace Robust.Client.Console.Commands
public string Help => "When no arguments are provided, displays a generic help text. When an argument is passed, display the help text for the command with that name.";
public string Description => "Display help text.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
console.AddLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
break;
case 1:
string commandname = args[0];
if (!console.Commands.ContainsKey(commandname))
if (!shell.ConsoleHost.RegisteredCommands.ContainsKey(commandname))
{
if (!IoCManager.Resolve<IClientNetManager>().IsConnected)
{
// No server so nothing to respond with unknown command.
console.AddLine("Unknown command: " + commandname, Color.Red);
return false;
shell.WriteLine("Unknown command: " + commandname, Color.Red);
return;
}
// TODO: Maybe have a server side help?
return false;
return;
}
IConsoleCommand command = console.Commands[commandname];
console.AddLine(string.Format("{0} - {1}", command.Command, command.Description));
console.AddLine(command.Help);
IConsoleCommand command = shell.ConsoleHost.RegisteredCommands[commandname];
shell.WriteLine(string.Format("{0} - {1}", command.Command, command.Description));
shell.WriteLine(command.Help);
break;
default:
console.AddLine("Invalid amount of arguments.", Color.Red);
shell.WriteLine("Invalid amount of arguments.", Color.Red);
break;
}
return false;
}
}
@@ -55,7 +54,7 @@ namespace Robust.Client.Console.Commands
"only commands that contain the given string in their name will be listed.";
public string Description => "List all commands, optionally with a filter.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var filter = "";
if (args.Length == 1)
@@ -64,14 +63,12 @@ namespace Robust.Client.Console.Commands
}
var conGroup = IoCManager.Resolve<IClientConGroupController>();
foreach (var command in console.Commands.Values
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
.Where(p => p.Command.Contains(filter) && conGroup.CanCommand(p.Command))
.OrderBy(c => c.Command))
{
console.AddLine(command.Command + ": " + command.Description);
shell.WriteLine(command.Command + ": " + command.Description);
}
return false;
}
}
}

View File

@@ -3,9 +3,9 @@ using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Robust.Client.Interfaces.Console;
using Robust.Client.Utility;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
@@ -17,7 +17,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Load authentication tokens from launcher data to aid in testing of live servers";
public string Help => "launchauth [account name]";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var wantName = args.Length > 0 ? args[0] : null;
@@ -32,8 +32,8 @@ namespace Robust.Client.Console.Commands
if (login == null)
{
console.AddLine("Unable to find a matching login");
return false;
shell.WriteLine("Unable to find a matching login");
return;
}
var token = login.Token.Token;
@@ -42,8 +42,6 @@ namespace Robust.Client.Console.Commands
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
cfg.SetSecureCVar(CVars.AuthUserId, userId);
cfg.SetSecureCVar(CVars.AuthToken, token);
return false;
}
private sealed class LauncherConfig

View File

@@ -2,7 +2,7 @@ using System.Linq;
using System.Runtime.Loader;
using System.Text;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,18 +13,16 @@ namespace Robust.Client.Console.Commands
public string Description => "Lists loaded assemblies by load context.";
public string Help => Command;
public bool Execute(IDebugConsole console, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
foreach (var context in AssemblyLoadContext.All)
{
console.AddLine($"{context.Name}:");
shell.WriteLine($"{context.Name}:");
foreach (var assembly in context.Assemblies.OrderBy(a => a.FullName))
{
console.AddLine($" {assembly.FullName}");
shell.WriteLine($" {assembly.FullName}");
}
}
return false;
}
}
}

View File

@@ -1,7 +1,7 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Log;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -13,12 +13,12 @@ namespace Robust.Client.Console.Commands
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
var name = args[0];
@@ -32,13 +32,12 @@ namespace Robust.Client.Console.Commands
{
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
level = result;
}
Logger.GetSawmill(name).Level = level;
return false;
}
}
@@ -51,12 +50,12 @@ namespace Robust.Client.Console.Commands
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
console.AddLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 3 arguments.", Color.Red);
return;
}
var name = args[0];
@@ -64,13 +63,12 @@ namespace Robust.Client.Console.Commands
var message = args[2]; // yes this doesn't support spaces idgaf.
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
console.AddLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return false;
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
var level = result;
Logger.LogS(level, name, message);
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
using Robust.Client.Interfaces.Console;
using System;
using Robust.Shared.Console;
namespace Robust.Client.Console.Commands
{
@@ -9,10 +9,9 @@ namespace Robust.Client.Console.Commands
public string Description => "Kills the game client instantly.";
public string Help => "Kills the game client instantly, leaving no traces. No telling the server goodbye";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
Environment.Exit(0);
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
@@ -6,17 +6,15 @@ using Robust.Shared.Maths;
namespace Robust.Client.Console.Commands
{
#if CLIENT_SCRIPTING
internal sealed class ScriptConsoleCommand : IConsoleCommand
internal sealed class ScriptCommand : IConsoleCommand
{
public string Command => "csi";
public string Description => "Opens a C# interactive console.";
public string Help => "csi";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new ScriptConsoleClient().OpenCentered();
return false;
}
}
@@ -26,33 +24,29 @@ namespace Robust.Client.Console.Commands
public string Description => "Opens a variable watch window.";
public string Help => "watch";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new WatchWindow().OpenCentered();
return false;
}
}
#endif
internal sealed class ServerScriptConsoleCommand : IConsoleCommand
internal sealed class ServerScriptCommand : 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)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mgr = IoCManager.Resolve<IScriptClient>();
if (!mgr.CanScript)
{
console.AddLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
return false;
shell.WriteLine(Loc.GetString("You do not have server side scripting permission."), Color.Red);
return;
}
mgr.StartSession();
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Network;
@@ -11,7 +11,7 @@ namespace Robust.Client.Console.Commands
public string Description => "Sends garbage to the server.";
public string Help => "The server will reply with 'no u'";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// MsgStringTableEntries is registered as NetMessageAccept.Client so the server will immediately deny it.
// And kick us.
@@ -19,8 +19,6 @@ namespace Robust.Client.Console.Commands
var msg = net.CreateNetMessage<MsgStringTableEntries>();
msg.Entries = new MsgStringTableEntries.Entry[0];
net.ClientSendMessage(msg);
return false;
}
}
}

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Input;
using Robust.Shared.Console;
using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
@@ -12,24 +12,23 @@ namespace Robust.Client.Console.Commands
public string Description => "Sets the active input context.";
public string Help => "setinputcontext <context>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid number of arguments!");
return false;
shell.WriteLine("Invalid number of arguments!");
return;
}
var inputMan = IoCManager.Resolve<IInputManager>();
if (!inputMan.Contexts.Exists(args[0]))
{
console.AddLine("Context not found!");
return false;
shell.WriteLine("Context not found!");
return;
}
inputMan.Contexts.SetActiveContext(args[0]);
return false;
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Client.Interfaces.Console;
namespace Robust.Client.Console
{
public interface IClientConsole : IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
event EventHandler ClearText;
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
/// <summary>
/// Parses console commands (verbs).
/// </summary>
/// <param name="text"></param>
void ProcessCommand(string text);
void SendServerCommandRequest();
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Robust.Shared.Console;
using Robust.Shared.Utility;
namespace Robust.Client.Console
{
public interface IClientConsoleHost : IConsoleHost, IDisposable
{
/// <summary>
/// Initializes the console into a useable state.
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;
void AddFormattedLine(FormattedMessage message);
}
}

View File

@@ -12,6 +12,7 @@ using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
@@ -26,6 +27,7 @@ namespace Robust.Client.Debugging
[Dependency] private readonly IComponentManager _componentManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPhysicsManager _physicsManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
@@ -48,7 +50,7 @@ namespace Robust.Client.Debugging
if (value)
{
_overlayManager.AddOverlay(new PhysicsOverlay(_componentManager, _eyeManager,
_prototypeManager, _inputManager));
_prototypeManager, _inputManager, _physicsManager));
}
else
{
@@ -86,6 +88,7 @@ namespace Robust.Client.Debugging
private readonly IComponentManager _componentManager;
private readonly IEyeManager _eyeManager;
private readonly IInputManager _inputManager;
private readonly IPhysicsManager _physicsManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
private readonly ShaderInstance _shader;
@@ -94,12 +97,13 @@ namespace Robust.Client.Debugging
private Vector2 _hoverStartScreen = Vector2.Zero;
private List<IPhysBody> _hoverBodies = new();
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager)
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager, IPhysicsManager physicsManager)
: base(nameof(PhysicsOverlay))
{
_componentManager = compMan;
_eyeManager = eyeMan;
_inputManager = inputManager;
_physicsManager = physicsManager;
_shader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
var cache = IoCManager.Resolve<IResourceCache>();
@@ -158,23 +162,20 @@ namespace Robust.Client.Debugging
_hoverStartScreen = mouseScreenPos;
var viewport = _eyeManager.GetWorldViewport();
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>())
{
var physBody = (IPhysBody) boundingBox;
if (viewport.IsEmpty()) return;
var mapId = _eyeManager.CurrentMap;
foreach (var physBody in _physicsManager.GetCollidingEntities(mapId, viewport))
{
// all entities have a TransformComponent
var transform = physBody.Entity.Transform;
// if not on the same map, continue
if (transform.MapID != _eyeManager.CurrentMap || !transform.IsMapTransform)
continue;
var worldBox = physBody.WorldAABB;
var colorEdge = Color.Red.WithAlpha(0.33f);
if (worldBox.IsEmpty()) continue;
// if not on screen, or too small, continue
if (!worldBox.Intersects(in viewport) || worldBox.IsEmpty())
continue;
var colorEdge = Color.Red.WithAlpha(0.33f);
foreach (var shape in physBody.PhysicsShapes)
{

View File

@@ -50,7 +50,7 @@ namespace Robust.Client
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IClientConsole _console = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IClientEntityManager _entityManager = default!;
[Dependency] private readonly IPlacementManager _placementManager = default!;
@@ -167,7 +167,7 @@ namespace Robust.Client
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
_serializer.Initialize();
_inputManager.Initialize();
_console.Initialize();
_consoleHost.Initialize();
_prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes/"));
_prototypeManager.Resync();
_mapManager.Initialize();

View File

@@ -55,15 +55,13 @@ namespace Robust.Client.GameObjects
Register<AppearanceComponent>();
RegisterReference<AppearanceComponent, SharedAppearanceComponent>();
Register<AppearanceTestComponent>();
Register<SnapGridComponent>();
Register<ClientUserInterfaceComponent>();
RegisterReference<ClientUserInterfaceComponent, SharedUserInterfaceComponent>();
RegisterIgnore("IgnorePause");
Register<AnimationPlayerComponent>();
Register<ContainerManagerComponent>();

View File

@@ -31,11 +31,11 @@ namespace Robust.Client.GameObjects
}
}
private static IDirectionalTextureProvider TextureForConfig(ObjectSerializer serializer, IResourceCache resourceCache)
private static IRsiStateLike TextureForConfig(ObjectSerializer serializer, IResourceCache resourceCache)
{
DebugTools.Assert(serializer.Reading);
if (serializer.TryGetCacheData<IDirectionalTextureProvider>(SerializationCache, out var dirTex))
if (serializer.TryGetCacheData<IRsiStateLike>(SerializationCache, out var dirTex))
{
return dirTex;
}
@@ -93,7 +93,7 @@ namespace Robust.Client.GameObjects
}
}
public static IDirectionalTextureProvider? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
public static IRsiStateLike? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
{
if (!prototype.Components.TryGetValue("Icon", out var mapping))
{

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
namespace Robust.Client.GameObjects.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedIgnorePauseComponent))]
public sealed class IgnorePauseComponent : SharedIgnorePauseComponent
{
}
}

View File

@@ -1046,7 +1046,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
var layerSpecificDir = layer.EffectiveDirection(state, worldRotation, overrideDirection);
@@ -1279,7 +1279,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
if (!state.IsAnimated)
@@ -1407,7 +1407,7 @@ namespace Robust.Client.GameObjects
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
if (state.IsAnimated)
@@ -1418,9 +1418,9 @@ namespace Robust.Client.GameObjects
}
}
private RSI.State GetFallbackState()
internal static RSI.State GetFallbackState(IResourceCache cache)
{
var rsi = resourceCache.GetResource<RSIResource>("/Textures/error.rsi").RSI;
var rsi = cache.GetResource<RSIResource>("/Textures/error.rsi").RSI;
return rsi["error"];
}
@@ -1738,14 +1738,14 @@ namespace Robust.Client.GameObjects
var rsi = ActualRsi;
if (rsi == null)
{
state = _parent.GetFallbackState();
state = GetFallbackState(_parent.resourceCache);
Logger.ErrorS(LogCategory, "No RSI to pull new state from! Trace:\n{0}", Environment.StackTrace);
}
else
{
if (!rsi.TryGetState(stateId, out state))
{
state = _parent.GetFallbackState();
state = GetFallbackState(_parent.resourceCache);
Logger.ErrorS(LogCategory, "State '{0}' does not exist in RSI. Trace:\n{1}", stateId,
Environment.StackTrace);
}
@@ -1796,7 +1796,7 @@ namespace Robust.Client.GameObjects
}
}
public IDirectionalTextureProvider? Icon
public IRsiStateLike? Icon
{
get
{
@@ -1806,13 +1806,13 @@ namespace Robust.Client.GameObjects
var texture = layer.Texture;
if (!layer.State.IsValid) return null;
if (!layer.State.IsValid) return texture;
// Pull texture from RSI state instead.
var rsi = layer.RSI ?? BaseRSI;
if (rsi == null || !rsi.TryGetState(layer.State, out var state))
{
state = GetFallbackState();
state = GetFallbackState(resourceCache);
}
return state;
@@ -1870,21 +1870,21 @@ namespace Robust.Client.GameObjects
}
public static IDirectionalTextureProvider? GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
public static IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache)
{
var icon = IconComponent.GetPrototypeIcon(prototype, resourceCache);
if (icon != null) return icon;
if (!prototype.Components.TryGetValue("Sprite", out var spriteNode))
if (!prototype.Components.ContainsKey("Sprite"))
{
return resourceCache.GetFallback<TextureResource>().Texture;
return GetFallbackState(resourceCache);
}
var dummy = new DummyIconEntity() {Prototype = prototype};
var dummy = new DummyIconEntity {Prototype = prototype};
var spriteComponent = dummy.AddComponent<SpriteComponent>();
dummy.Delete();
return spriteComponent?.Icon ?? resourceCache.GetFallback<TextureResource>().Texture;
return spriteComponent.Icon ?? GetFallbackState(resourceCache);
}
#region DummyIconEntity

View File

@@ -7,7 +7,7 @@ namespace Robust.Client.GameObjects.EntitySystems
{
public override void FrameUpdate(float frameTime)
{
foreach (var animationPlayerComponent in EntityManager.ComponentManager.EntityQuery<AnimationPlayerComponent>())
foreach (var animationPlayerComponent in EntityManager.ComponentManager.EntityQuery<AnimationPlayerComponent>(true))
{
animationPlayerComponent.Update(frameTime);
}

View File

@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects.EntitySystems
{
public override void Update(float frameTime)
{
foreach (var appearanceTestComponent in EntityManager.ComponentManager.EntityQuery<AppearanceTestComponent>())
foreach (var appearanceTestComponent in EntityManager.ComponentManager.EntityQuery<AppearanceTestComponent>(true))
{
appearanceTestComponent.OnUpdate(frameTime);
}

View File

@@ -56,12 +56,12 @@ namespace Robust.Client.GameObjects.EntitySystems
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
{
var gridId = ev.Coordinates.GetGridId(_entityManager);
if (!_mapManager.GridExists(gridId))
var mapId = ev.Coordinates.GetMapId(_entityManager);
if (!_mapManager.MapExists(mapId))
{
Logger.Error(
$"Server tried to play sound on grid {gridId}, which does not exist. Ignoring.");
$"Server tried to play sound on map {mapId}, which does not exist. Ignoring.");
return;
}
@@ -111,13 +111,13 @@ namespace Robust.Client.GameObjects.EntitySystems
if (stream.TrackingCoordinates != null)
{
var coords = stream.TrackingCoordinates.Value;
if (_mapManager.GridExists(coords.GetGridId(_entityManager)))
if (_mapManager.MapExists(coords.GetMapId(_entityManager)))
{
mapPos = stream.TrackingCoordinates.Value.ToMap(_entityManager);
}
else
{
// Grid no longer exists, delete stream.
// Map no longer exists, delete stream.
StreamDone(stream);
continue;
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Graphics;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.ResourceManagement;
@@ -57,47 +57,54 @@ namespace Robust.Client.GameObjects
public void CreateEffect(EffectSystemMessage message)
{
// The source of effects is either local actions during FirstTimePredicted, or the network at LastServerTick
// When replaying predicted input, don't spam effects.
if(gameTiming.InPrediction && !gameTiming.IsFirstTimePredicted)
return;
if (message.AttachedEntityUid != null && message.Coordinates != default)
{
Logger.Warning("Set both an AttachedEntityUid and EntityCoordinates on an EffectSystemMessage for sprite {0} which is not supported!", message.EffectSprite);
}
var gameTime = gameTiming.CurTime;
if (gameTime > message.DeathTime) //Did we already die in transit? That's pretty troubling isn't it
if (message.LifeTime <= TimeSpan.Zero)
{
Logger.Warning("Effect using sprite {0} died in transit to the client", message.EffectSprite);
Logger.Warning("Effect using sprite {0} had zero lifetime.", message.EffectSprite);
return;
}
//Create effect from creation message
var effect = new Effect(message, resourceCache, _mapManager, _entityManager);
effect.Deathtime = gameTiming.CurTime + message.LifeTime;
if (effect.AttachedEntityUid != null)
{
effect.AttachedEntity = _entityManager.GetEntity(effect.AttachedEntityUid.Value);
}
//Age the effect through a single update to the previous update tick of the effect system
//effect.Update((float)((lasttimeprocessed - effect.Age).TotalSeconds));
_Effects.Add(effect);
}
public override void FrameUpdate(float frameTime)
{
var curTime = gameTiming.CurTime;
for (int i = 0; i < _Effects.Count; i++)
{
var effect = _Effects[i];
//Update variables of the effect via its deltas
effect.Update(frameTime);
//These effects have died
if (effect.Age > effect.Deathtime)
// Effects are purely visual, so they don't need to be ran through prediction.
// once CurTime ever passes DeathTime (clients render the top at IsFirstTimePredicted, where this happens) just remove them.
if (curTime > effect.Deathtime)
{
//Remove from the effects list and decrement the iterator
_Effects.Remove(effect);
i--;
}
else
{
//Update variables of the effect via its deltas
effect.Update(frameTime);
}
}
}
@@ -205,14 +212,9 @@ namespace Robust.Client.GameObjects
public bool Shaded = true;
/// <summary>
/// Effect's age -- from 0f
/// CurTime after which the effect will "die"
/// </summary>
public TimeSpan Age = TimeSpan.Zero;
/// <summary>
/// Time after which the effect will "die"
/// </summary>
public TimeSpan Deathtime = TimeSpan.FromSeconds(1);
public TimeSpan Deathtime;
private readonly IMapManager _mapManager;
private readonly IEntityManager _entityManager;
@@ -245,8 +247,6 @@ namespace Robust.Client.GameObjects
RadialAcceleration = effectcreation.RadialAcceleration;
TangentialVelocity = effectcreation.TangentialVelocity;
TangentialAcceleration = effectcreation.TangentialAcceleration;
Age = effectcreation.Born;
Deathtime = effectcreation.DeathTime;
Rotation = effectcreation.Rotation;
RotationRate = effectcreation.RotationRate;
Size = effectcreation.Size;
@@ -260,10 +260,6 @@ namespace Robust.Client.GameObjects
public void Update(float frameTime)
{
Age += TimeSpan.FromSeconds(frameTime);
if (Age >= Deathtime)
return;
Velocity += Acceleration * frameTime;
RadialVelocity += RadialAcceleration * frameTime;
TangentialVelocity += TangentialAcceleration * frameTime;

View File

@@ -87,7 +87,7 @@ namespace Robust.Client.GameObjects.EntitySystems
}
}
foreach (var eyeComponent in EntityManager.ComponentManager.EntityQuery<EyeComponent>())
foreach (var eyeComponent in EntityManager.ComponentManager.EntityQuery<EyeComponent>(true))
{
eyeComponent.UpdateEyePosition();
}

View File

@@ -0,0 +1,262 @@
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
namespace Robust.Client.GameStates
{
/// <summary>
/// A network entity report that lists all entities as they are updated through game states.
/// https://developer.valvesoftware.com/wiki/Networking_Entities#cl_entityreport
/// </summary>
class NetEntityOverlay : Overlay
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
private const int TrafficHistorySize = 64; // Size of the traffic history bar in game ticks.
/// <inheritdoc />
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
private readonly Font _font;
private readonly int _lineHeight;
private readonly List<NetEntity> _netEnts = new();
public NetEntityOverlay() : base(nameof(NetEntityOverlay))
{
IoCManager.InjectDependencies(this);
var cache = IoCManager.Resolve<IResourceCache>();
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
_lineHeight = _font.GetLineHeight(1);
_gameStateManager.GameStateApplied += HandleGameStateApplied;
}
private void HandleGameStateApplied(GameStateAppliedArgs args)
{
if(_gameTiming.InPrediction) // we only care about real server states.
return;
// Shift traffic history down one
for (var i = 0; i < _netEnts.Count; i++)
{
var traffic = _netEnts[i].Traffic;
for (int j = 1; j < TrafficHistorySize; j++)
{
traffic[j - 1] = traffic[j];
}
traffic[^1] = 0;
}
var gameState = args.AppliedState;
if(gameState.EntityStates is not null)
{
// Loop over every entity that gets updated this state and record the traffic
foreach (var entityState in gameState.EntityStates)
{
var newEnt = true;
for(var i=0;i<_netEnts.Count;i++)
{
var netEnt = _netEnts[i];
if (netEnt.Id != entityState.Uid)
continue;
//TODO: calculate size of state and record it here.
netEnt.Traffic[^1] = 1;
netEnt.LastUpdate = gameState.ToSequence;
newEnt = false;
_netEnts[i] = netEnt; // copy struct back
break;
}
if (!newEnt)
continue;
var newNetEnt = new NetEntity(entityState.Uid);
newNetEnt.Traffic[^1] = 1;
newNetEnt.LastUpdate = gameState.ToSequence;
_netEnts.Add(newNetEnt);
}
}
bool pvsEnabled = _configurationManager.GetCVar<bool>("net.pvs");
float pvsSize = _configurationManager.GetCVar<float>("net.maxupdaterange");
var pvsCenter = _eyeManager.CurrentEye.Position;
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsSize*2, pvsSize*2));
int timeout = _gameTiming.TickRate * 3;
for (int i = 0; i < _netEnts.Count; i++)
{
var netEnt = _netEnts[i];
if(_entityManager.EntityExists(netEnt.Id))
{
//TODO: Whoever is working on PVS remake, change the InPVS detection.
var position = _entityManager.GetEntity(netEnt.Id).Transform.MapPosition;
netEnt.InPVS = !pvsEnabled || (pvsBox.Contains(position.Position) && position.MapId == pvsCenter.MapId);
_netEnts[i] = netEnt; // copy struct back
continue;
}
netEnt.Exists = false;
if (netEnt.LastUpdate.Value + timeout < _gameTiming.LastRealTick.Value)
{
_netEnts.RemoveAt(i);
i--;
continue;
}
_netEnts[i] = netEnt; // copy struct back
}
}
protected override void Draw(DrawingHandleBase handle, OverlaySpace currentSpace)
{
if (!_netManager.IsConnected)
return;
// remember, 0,0 is top left of ui with +X right and +Y down
var screenHandle = (DrawingHandleScreen)handle;
for (int i = 0; i < _netEnts.Count; i++)
{
var netEnt = _netEnts[i];
var xPos = 100;
var yPos = 10 + _lineHeight * i;
var name = $"({netEnt.Id}) {_entityManager.GetEntity(netEnt.Id).Prototype?.ID}";
var color = CalcTextColor(ref netEnt);
DrawString(screenHandle, _font, new Vector2(xPos + (TrafficHistorySize + 4), yPos), name, color);
DrawTrafficBox(screenHandle, ref netEnt, xPos, yPos);
}
}
private void DrawTrafficBox(DrawingHandleScreen handle, ref NetEntity netEntity, int x, int y)
{
handle.DrawRect(UIBox2.FromDimensions(x+1, y, TrafficHistorySize + 1, _lineHeight), new Color(32, 32, 32, 128));
handle.DrawRect(UIBox2.FromDimensions(x, y, TrafficHistorySize + 2, _lineHeight), Color.Gray.WithAlpha(0.15f), false);
var traffic = netEntity.Traffic;
//TODO: Local peak size, actually scale the peaks
for (int i = 0; i < TrafficHistorySize; i++)
{
if(traffic[i] == 0)
continue;
var xPos = x + 1 + i;
var yPosA = y + 1;
var yPosB = yPosA + _lineHeight - 1;
handle.DrawLine(new Vector2(xPos, yPosA), new Vector2(xPos, yPosB), Color.Green);
}
}
private Color CalcTextColor(ref NetEntity ent)
{
if(!ent.Exists)
return Color.Gray; // Entity is deleted, will be removed from list soon.
if(!ent.InPVS)
return Color.Red; // Entity still exists outside PVS, but not updated anymore.
if(_gameTiming.LastRealTick < ent.LastUpdate + _gameTiming.TickRate)
return Color.Blue; //Entity in PVS generating ongoing traffic.
return Color.Green; // Entity in PVS, but not updated recently.
}
protected override void Dispose(bool disposing)
{
_gameStateManager.GameStateApplied -= HandleGameStateApplied;
base.Dispose(disposing);
}
private static void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str, Color textColor)
{
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
foreach (var chr in str)
{
var advance = font.DrawChar(handle, chr, baseLine, 1, textColor);
baseLine += new Vector2(advance, 0);
}
}
private struct NetEntity
{
public GameTick LastUpdate;
public readonly EntityUid Id;
public readonly int[] Traffic;
public bool Exists;
public bool InPVS;
public NetEntity(EntityUid id)
{
LastUpdate = GameTick.Zero;
Id = id;
Traffic = new int[TrafficHistorySize];
Exists = true;
InPVS = true;
}
}
private class NetEntityReportCommand : IConsoleCommand
{
public string Command => "net_entityreport";
public string Help => "net_entityreport <0|1>";
public string Description => "Toggles the net entity report panel.";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
if (!byte.TryParse(args[0], out var iValue))
{
shell.WriteLine("Invalid argument: Needs to be 0 or 1.", Color.Red);
return;
}
var bValue = iValue > 0;
var overlayMan = IoCManager.Resolve<IOverlayManager>();
if(bValue && !overlayMan.HasOverlay(nameof(NetEntityOverlay)))
{
overlayMan.AddOverlay(new NetEntityOverlay());
shell.WriteLine("Enabled network entity report overlay.");
}
else if(!bValue && overlayMan.HasOverlay(nameof(NetEntityOverlay)))
{
overlayMan.RemoveOverlay(nameof(NetEntityOverlay));
shell.WriteLine("Disabled network entity report overlay.");
}
}
}
}
}

View File

@@ -2,11 +2,11 @@
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.GameStates;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -172,18 +172,18 @@ namespace Robust.Client.GameStates
public string Help => "net_graph <0|1>";
public string Description => "Toggles the net statistics pannel.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
if (!byte.TryParse(args[0], out var iValue))
{
console.AddLine("Invalid argument: Needs to be 0 or 1.");
return false;
shell.WriteLine("Invalid argument: Needs to be 0 or 1.");
return;
}
var bValue = iValue > 0;
@@ -192,15 +192,13 @@ namespace Robust.Client.GameStates
if(bValue && !overlayMan.HasOverlay(nameof(NetGraphOverlay)))
{
overlayMan.AddOverlay(new NetGraphOverlay());
console.AddLine("Enabled network overlay.");
shell.WriteLine("Enabled network overlay.");
}
else if(overlayMan.HasOverlay(nameof(NetGraphOverlay)))
{
overlayMan.RemoveOverlay(nameof(NetGraphOverlay));
console.AddLine("Disabled network overlay.");
shell.WriteLine("Disabled network overlay.");
}
return false;
}
}
}

View File

@@ -1,9 +1,9 @@
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.Console;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
@@ -34,7 +34,7 @@ namespace Robust.Client.GameStates
handle.UseShader(_shader);
var worldHandle = (DrawingHandleWorld) handle;
var viewport = _eyeManager.GetWorldViewport();
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>())
foreach (var boundingBox in _componentManager.EntityQuery<IPhysicsComponent>(true))
{
// all entities have a TransformComponent
var transform = ((IComponent)boundingBox).Owner.Transform;
@@ -73,18 +73,18 @@ namespace Robust.Client.GameStates
public string Help => "net_draw_interp <0|1>";
public string Description => "Toggles the debug drawing of the network interpolation.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
console.AddLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return false;
shell.WriteLine("Invalid argument amount. Expected 2 arguments.", Color.Red);
return;
}
if (!byte.TryParse(args[0], out var iValue))
{
console.AddLine("Invalid argument: Needs to be 0 or 1.");
return false;
shell.WriteLine("Invalid argument: Needs to be 0 or 1.");
return;
}
var bValue = iValue > 0;
@@ -93,15 +93,13 @@ namespace Robust.Client.GameStates
if (bValue && !overlayMan.HasOverlay(nameof(NetInterpOverlay)))
{
overlayMan.AddOverlay(new NetInterpOverlay());
console.AddLine("Enabled network interp overlay.");
shell.WriteLine("Enabled network interp overlay.");
}
else if (overlayMan.HasOverlay(nameof(NetInterpOverlay)))
{
overlayMan.RemoveOverlay(nameof(NetInterpOverlay));
console.AddLine("Disabled network interp overlay.");
shell.WriteLine("Disabled network interp overlay.");
}
return false;
}
}
}

View File

@@ -27,8 +27,6 @@ namespace Robust.Client.Graphics.Clyde
private const string UniProjViewMatrices = "projectionViewMatrices";
private const string UniUniformConstants = "uniformConstants";
private static readonly Color AmbientLightColor = Color.Black;
private const int BindingIndexProjView = 0;
private const int BindingIndexUniformConstants = 1;

View File

@@ -77,7 +77,7 @@ namespace Robust.Client.Graphics.Clyde
{
// OpenGL ES capabilities.
CheckGLCap(ref _hasGLKhrDebug, "khr_debug", (3, 2), "GL_KHR_debug");
if (CompareVersion(3, 2, major, minor))
if (!CompareVersion(3, 2, major, minor))
{
// We're ES <3.2, KHR_debug is extension and needs KHR suffixes.
_isGLKhrDebugESExtension = true;

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Buffers;
using OpenToolkit.Graphics.OpenGL4;
using Robust.Client.GameObjects;
@@ -30,7 +31,11 @@ namespace Robust.Client.Graphics.Clyde
// Horizontal width, in pixels, of the shadow maps used to render FOV.
// I figured this was more accuracy sensitive than lights so resolution is significantly higher.
private const int FovMapSize = 2048;
private const int MaxLightsPerScene = 128;
// The maximum possible amount of lights in the light list.
// In the average case, the only cost of increasing this value is memory.
// If you are ever in a situation where this value needs to be increased, however, it will also implicitly cost some CPU time to sort the additional lights.
private const int LightsToRenderListSize = 2048;
private ClydeShaderInstance _fovDebugShaderInstance = default!;
@@ -80,16 +85,18 @@ namespace Robust.Client.Graphics.Clyde
// For depth calculation of lighting shadows.
private RenderTexture _shadowRenderTarget = default!;
// Used because otherwise a MaxLightsPerScene change callback getting hit on startup causes interesting issues (read: bugs)
private bool _shadowRenderTargetCanInitializeSafely = false;
// Proxies to textures of the above render targets.
private ClydeTexture FovTexture => _fovRenderTarget.Texture;
private ClydeTexture ShadowTexture => _shadowRenderTarget.Texture;
private readonly (PointLightComponent light, Vector2 pos)[] _lightsToRenderList
= new (PointLightComponent light, Vector2 pos)[MaxLightsPerScene];
private (PointLightComponent light, Vector2 pos, float distanceSquared)[] _lightsToRenderList = new (PointLightComponent light, Vector2 pos, float distanceSquared)[LightsToRenderListSize];
private unsafe void InitLighting()
{
// Other...
LoadLightingShaders();
{
@@ -160,10 +167,8 @@ namespace Robust.Client.Graphics.Clyde
}
// Shadow FBO.
_shadowRenderTarget = CreateRenderTarget((ShadowMapSize, MaxLightsPerScene),
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat, Filter = true},
nameof(_shadowRenderTarget));
_shadowRenderTargetCanInitializeSafely = true;
MaxLightsPerSceneChanged(_maxLightsPerScene);
}
private void LoadLightingShaders()
@@ -334,6 +339,14 @@ namespace Robust.Client.Graphics.Clyde
DrawFov(viewport, eye);
if (!_lightManager.DrawLighting)
{
BindRenderTargetFull(viewport.RenderTarget);
GL.Viewport(0, 0, viewport.Size.X, viewport.Size.Y);
CheckGlError();
return;
}
using (DebugGroup("Draw shadow depth"))
{
PrepareDepthDraw(RtToLoaded(_shadowRenderTarget));
@@ -344,7 +357,7 @@ namespace Robust.Client.Graphics.Clyde
{
for (var i = 0; i < count; i++)
{
var (light, lightPos) = lights[i];
var (light, lightPos, _) = lights[i];
DrawOcclusionDepth(lightPos, ShadowMapSize, light.Radius, i);
}
@@ -355,7 +368,7 @@ namespace Robust.Client.Graphics.Clyde
BindRenderTargetImmediate(RtToLoaded(viewport.LightRenderTarget));
CheckGlError();
GLClearColor(Color.FromSrgb(AmbientLightColor));
GLClearColor(_lightManager.AmbientLightColor);
GL.Clear(ClearBufferMask.ColorBufferBit);
CheckGlError();
@@ -382,7 +395,7 @@ namespace Robust.Client.Graphics.Clyde
for (var i = 0; i < count; i++)
{
var (component, lightPos) = lights[i];
var (component, lightPos, _) = lights[i];
var transform = component.Owner.Transform;
@@ -473,25 +486,24 @@ namespace Robust.Client.Graphics.Clyde
_lightingReady = true;
}
private ((PointLightComponent light, Vector2 pos)[] lights, int count, Box2 expandedBounds)
private ((PointLightComponent light, Vector2 pos, float distanceSquared)[] lights, int count, Box2 expandedBounds)
GetLightsToRender(MapId map, in Box2 worldBounds)
{
// When culling occluders later, we can't just remove any occluders outside the worldBounds.
// As they could still affect the shadows of (large) light sources.
// We expand the world bounds so that it encompasses the center of every light source.
// This should make it so no culled occluder can make a difference.
// (if the occluder is in the current lights at all, it's still not between the light and the world bounds).
var expandedBounds = worldBounds;
var renderingTreeSystem = _entitySystemManager.GetEntitySystem<RenderingTreeSystem>();
var lightTree = renderingTreeSystem.GetLightTreeForMap(map);
var state = (this, expandedBounds, count: 0);
var state = (this, worldBounds, count: 0);
lightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 expandedBounds, int count) state, in PointLightComponent light) =>
lightTree.QueryAabb(ref state, (ref (Clyde clyde, Box2 worldBounds, int count) state, in PointLightComponent light) =>
{
var transform = light.Owner.Transform;
if (state.count >= LightsToRenderListSize)
{
// There are too many lights to fit in the static memory.
return false;
}
if (!light.Enabled || light.ContainerOccluded)
{
return true;
@@ -501,26 +513,45 @@ namespace Robust.Client.Graphics.Clyde
var circle = new Circle(lightPos, light.Radius);
if (!circle.Intersects(state.expandedBounds))
// If the light doesn't touch anywhere the camera can see, it doesn't matter.
if (!circle.Intersects(state.worldBounds))
{
return true;
}
state.clyde._lightsToRenderList[state.count] = (light, lightPos);
state.count += 1;
state.expandedBounds = state.expandedBounds.ExtendToContain(lightPos);
if (state.count == MaxLightsPerScene)
{
// TODO: Allow more than MaxLightsPerScene lights.
return false;
}
float distanceSquared = (state.worldBounds.Center - lightPos).LengthSquared;
state.clyde._lightsToRenderList[state.count++] = (light, lightPos, distanceSquared);
return true;
}, expandedBounds);
}, worldBounds);
return (_lightsToRenderList, state.count, state.expandedBounds);
if (state.count > _maxLightsPerScene)
{
// There are too many lights to fit in the scene.
// This check must occur before occluder expansion, or else bad things happen.
// Sort lights by distance.
Array.Sort(_lightsToRenderList, 0, state.count, Comparer<(PointLightComponent light, Vector2 pos, float distanceSquared)>.Create((x, y) =>
{
return x.distanceSquared.CompareTo(y.distanceSquared);
}));
// Then effectively delete the furthest lights.
state.count = _maxLightsPerScene;
}
// When culling occluders later, we can't just remove any occluders outside the worldBounds.
// As they could still affect the shadows of (large) light sources.
// We expand the world bounds so that it encompasses the center of every light source.
// This should make it so no culled occluder can make a difference.
// (if the occluder is in the current lights at all, it's still not between the light and the world bounds).
var expandedBounds = worldBounds;
for (var i = 0; i < state.count; i++)
{
var (_, lightPos, _) = _lightsToRenderList[i];
expandedBounds = expandedBounds.ExtendToContain(lightPos);
}
return (_lightsToRenderList, state.count, expandedBounds);
}
private void BlurOntoWalls(Viewport viewport, IEye eye)
@@ -959,6 +990,25 @@ namespace Robust.Client.Graphics.Clyde
RegenAllLightRts();
}
protected override void MaxLightsPerSceneChanged(int newValue)
{
_maxLightsPerScene = newValue;
// This guard is in place because otherwise the shadow FBO is initialized before GL is initialized.
if (!_shadowRenderTargetCanInitializeSafely)
return;
if (_shadowRenderTarget != null)
{
DeleteRenderTexture(_shadowRenderTarget.Handle);
}
// Shadow FBO.
_shadowRenderTarget = CreateRenderTarget((ShadowMapSize, _maxLightsPerScene),
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat, Filter = true},
nameof(_shadowRenderTarget));
}
protected override void SoftShadowsChanged(bool newValue)
{
_enableSoftShadows = newValue;

View File

@@ -800,7 +800,7 @@ namespace Robust.Client.Graphics.Clyde
return;
}
GL.CreateBuffers(1, out uint pbo);
GL.GenBuffers(1, out uint pbo);
CheckGlError();
GL.BindBuffer(BufferTarget.PixelPackBuffer, pbo);
CheckGlError();

View File

@@ -63,6 +63,7 @@ namespace Robust.Client.Graphics.Clyde
private GLShaderProgram? _currentProgram;
private int _lightmapDivider = 2;
private int _maxLightsPerScene = 128;
private bool _enableSoftShadows = true;
private bool _checkGLErrors;
@@ -133,6 +134,7 @@ namespace Robust.Client.Graphics.Clyde
{
base.ReadConfig();
_lightmapDivider = _configurationManager.GetCVar(CVars.DisplayLightMapDivider);
_maxLightsPerScene = _configurationManager.GetCVar(CVars.DisplayMaxLightsPerScene);
_enableSoftShadows = _configurationManager.GetCVar(CVars.DisplaySoftShadows);
}

View File

@@ -34,6 +34,7 @@ namespace Robust.Client.Graphics
_configurationManager.OnValueChanged(CVars.DisplayVSync, _vSyncChanged, true);
_configurationManager.OnValueChanged(CVars.DisplayWindowMode, _windowModeChanged, true);
_configurationManager.OnValueChanged(CVars.DisplayLightMapDivider, LightmapDividerChanged, true);
_configurationManager.OnValueChanged(CVars.DisplayMaxLightsPerScene, MaxLightsPerSceneChanged, true);
_configurationManager.OnValueChanged(CVars.DisplaySoftShadows, SoftShadowsChanged, true);
return true;
@@ -76,6 +77,10 @@ namespace Robust.Client.Graphics
{
}
protected virtual void MaxLightsPerSceneChanged(int newValue)
{
}
protected virtual void SoftShadowsChanged(bool newValue)
{
}

View File

@@ -0,0 +1,12 @@
namespace Robust.Client.Graphics
{
public interface IRsiStateLike : IDirectionalTextureProvider
{
RSI.State.DirectionType Directions { get; }
bool IsAnimated { get; }
int AnimationFrameCount { get; }
float GetDelay(int frame);
Texture GetFrame(RSI.State.Direction dir, int frame);
}
}

View File

@@ -1,4 +1,5 @@
using Robust.Client.Interfaces.Graphics.Lighting;
using Robust.Shared.Maths;
namespace Robust.Client.Graphics.Lighting
{
@@ -7,5 +8,8 @@ namespace Robust.Client.Graphics.Lighting
public bool Enabled { get; set; } = true;
public bool DrawShadows { get; set; } = true;
public bool DrawHardFov { get; set; } = true;
public bool DrawLighting { get; set; } = true;
public bool LockConsoleAccess { get; set; } = false;
public Color AmbientLightColor { get; set; } = Color.FromSrgb(Color.Black);
}
}

View File

@@ -16,7 +16,7 @@ namespace Robust.Client.Graphics
/// RSIs are folded into a single set of animation timings when loaded.
/// This is to simplify animation playback code in-engine.
/// </remarks>
public sealed class State : IDirectionalTextureProvider
public sealed class State : IRsiStateLike
{
// List of delays for the frame to reach the next frame.
private readonly float[] Delays;
@@ -80,6 +80,8 @@ namespace Robust.Client.Graphics
/// </summary>
public bool IsAnimated => DelayCount > 1;
int IRsiStateLike.AnimationFrameCount => DelayCount;
public Texture GetFrame(Direction direction, int frame)
{
return Icons[(int) direction][frame];

View File

@@ -15,7 +15,7 @@ namespace Robust.Client.Graphics
/// Contains a texture used for drawing things.
/// </summary>
[PublicAPI]
public abstract class Texture : IDirectionalTextureProvider
public abstract class Texture : IRsiStateLike
{
/// <summary>
/// The width of the texture, in pixels.
@@ -85,6 +85,26 @@ namespace Robust.Client.Graphics
{
return this;
}
RSI.State.DirectionType IRsiStateLike.Directions => RSI.State.DirectionType.Dir1;
bool IRsiStateLike.IsAnimated => false;
int IRsiStateLike.AnimationFrameCount => 0;
float IRsiStateLike.GetDelay(int frame)
{
if (frame != 0)
throw new IndexOutOfRangeException();
return 0;
}
Texture IRsiStateLike.GetFrame(RSI.State.Direction dir, int frame)
{
if (frame != 0)
throw new IndexOutOfRangeException();
return this;
}
}
/// <summary>

View File

@@ -7,9 +7,9 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.Reflection;
@@ -812,34 +812,34 @@ namespace Robust.Client.Input
public string Description => "Binds an input key to an input command.";
public string Help => "bind <KeyName> <BindMode> <InputCommand>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 3)
{
console.AddLine("Too few arguments.");
return false;
shell.WriteLine("Too few arguments.");
return;
}
if (args.Length > 3)
{
console.AddLine("Too many arguments.");
return false;
shell.WriteLine("Too many arguments.");
return;
}
var keyName = args[0];
if (!Enum.TryParse(typeof(Key), keyName, true, out var keyIdObj))
{
console.AddLine($"Key '{keyName}' is unrecognized.");
return false;
shell.WriteLine($"Key '{keyName}' is unrecognized.");
return;
}
var keyId = (Key) keyIdObj!;
if (!Enum.TryParse(typeof(KeyBindingType), args[1], true, out var keyModeObj))
{
console.AddLine($"BindMode '{args[1]}' is unrecognized.");
return false;
shell.WriteLine($"BindMode '{args[1]}' is unrecognized.");
return;
}
var keyMode = (KeyBindingType) keyModeObj!;
@@ -856,8 +856,6 @@ namespace Robust.Client.Input
};
inputMan.RegisterBinding(registration);
return false;
}
}
@@ -868,12 +866,10 @@ namespace Robust.Client.Input
public string Description => "";
public string Help => "";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IInputManager>()
.SaveToUserData();
return false;
}
}
}

View File

@@ -1,13 +0,0 @@
using Robust.Shared.Console;
namespace Robust.Client.Interfaces.Console
{
public interface IConsoleCommand : ICommand
{
/// <summary>
/// Executes the command
/// </summary>
/// <returns>Whether or not the command should also be forwarded to the server. True to allow forwarding, false to block.</returns>
bool Execute(IDebugConsole console, params string[] args);
}
}

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
using Robust.Shared.Maths;
using Robust.Shared;
using Robust.Shared.Console;
using Robust.Client.Utility;
using Robust.Shared.Utility;
namespace Robust.Client.Interfaces.Console
{
public interface IDebugConsole
{
IReadOnlyDictionary<string, IConsoleCommand> Commands { get; }
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
void AddLine(string text, Color color);
void AddLine(string text);
void AddFormattedLine(FormattedMessage message);
void Clear();
}
}

View File

@@ -1,9 +1,32 @@
using Robust.Shared.Maths;
namespace Robust.Client.Interfaces.Graphics.Lighting
{
public interface ILightManager
{
/// <summary>
/// Enables/disables the entire light manager.
/// </summary>
bool Enabled { get; set; }
/// <summary>
/// Enables/disables shadows, but lights are still functional.
/// </summary>
bool DrawShadows { get; set; }
/// <summary>
/// Enables/disables hard FOV.
/// </summary>
bool DrawHardFov { get; set; }
/// <summary>
/// Enables/disables everything to do with the lighting buffer, without interfering with hard FOV.
/// </summary>
bool DrawLighting { get; set; }
/// <summary>
/// This is useful to prevent players messing with lighting setup when they shouldn't.
/// </summary>
bool LockConsoleAccess { get; set; }
/// <summary>
/// Ambient light. This is in linear-light, i.e. when providing a fixed colour, you must use Color.FromSrgb(Color.Black)!
/// </summary>
Color AmbientLightColor { get; set; }
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.Interfaces.Console;
using Robust.Client.Console;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Log;
using Robust.Shared.Maths;
@@ -12,9 +12,9 @@ namespace Robust.Client.Log
/// </summary>
class DebugConsoleLogHandler : ILogHandler
{
readonly IDebugConsole Console;
readonly IClientConsoleHost Console;
public DebugConsoleLogHandler(IDebugConsole console)
public DebugConsoleLogHandler(IClientConsoleHost console)
{
Console = console;
}

View File

@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.166" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="nfluidsynth" Version="0.3.0" />
<PackageReference Include="nfluidsynth" Version="0.3.1" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NJsonSchema" Version="10.3.1" Condition="'$(Configuration)' == 'Debug'" />

View File

@@ -33,6 +33,7 @@ namespace Robust.Client.UserInterface
}
public event Action<GUIBoundKeyEventArgs>? OnKeyBindDown;
public event Action<GUIBoundKeyEventArgs>? OnKeyBindUp;
protected internal virtual void KeyBindDown(GUIBoundKeyEventArgs args)
{
@@ -41,6 +42,7 @@ namespace Robust.Client.UserInterface
protected internal virtual void KeyBindUp(GUIBoundKeyEventArgs args)
{
OnKeyBindUp?.Invoke(args);
}
protected internal virtual void MouseMove(GUIMouseMoveEventArgs args)

View File

@@ -0,0 +1,62 @@
using Robust.Client.Graphics;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.Controls
{
/// <summary>
/// A more complex control wrapping <see cref="TextureRect"/> that can do RSI directions and animations.
/// </summary>
public sealed class AnimatedTextureRect : Control
{
private IRsiStateLike? _state;
private int _curFrame;
private float _curFrameTime;
/// <summary>
/// Internal TextureRect used to do actual drawing of the texture.
/// You can use this property to change shaders or styling or such.
/// </summary>
public TextureRect DisplayRect { get; }
public RSI.State.Direction RsiDirection { get; } = RSI.State.Direction.South;
public AnimatedTextureRect()
{
IoCManager.InjectDependencies(this);
DisplayRect = new TextureRect();
AddChild(DisplayRect);
}
public void SetFromSpriteSpecifier(SpriteSpecifier specifier)
{
_curFrame = 0;
_state = specifier.RsiStateLike();
_curFrameTime = _state.GetDelay(0);
DisplayRect.Texture = _state.GetFrame(RsiDirection, 0);
}
protected override void FrameUpdate(FrameEventArgs args)
{
if (!VisibleInTree || _state == null || !_state.IsAnimated)
return;
var oldFrame = _curFrame;
_curFrameTime -= args.DeltaSeconds;
while (_curFrameTime < _state.GetDelay(_curFrame))
{
_curFrame = (_curFrame + 1) % _state.AnimationFrameCount;
_curFrameTime += _state.GetDelay(_curFrame);
}
if (_curFrame != oldFrame)
{
DisplayRect.Texture = _state.GetFrame(RsiDirection, _curFrame);
}
}
}
}

View File

@@ -446,8 +446,8 @@ namespace Robust.Client.UserInterface.Controls
var (vSepActual, hSepActual) = (Vector2i) (Separations * UIScale);
var hSep = _limitDimension == Dimension.Column ? hSepActual : vSepActual;
var vSep = _limitDimension == Dimension.Column ? vSepActual : hSepActual;
var width = _limitDimension == Dimension.Column ? Width : Height;
var height = _limitDimension == Dimension.Column ? Height : Width;
var width = _limitDimension == Dimension.Column ? PixelWidth : PixelHeight;
var height = _limitDimension == Dimension.Column ? PixelHeight : PixelWidth;
var stretchMaxX = width - hSep * (cols - 1);
var stretchMaxY = height - vSep * (rows - 1);

View File

@@ -1,4 +1,3 @@
using JetBrains.Annotations;
using Robust.Client.Graphics.Drawing;
using Robust.Shared.Maths;
@@ -20,7 +19,7 @@ namespace Robust.Client.UserInterface.Controls
protected override void LayoutUpdateOverride()
{
var contentBox = _getStyleBox()?.GetContentBox(PixelSizeBox) ?? SizeBox;
var contentBox = _getStyleBox()?.GetContentBox(PixelSizeBox) ?? PixelSizeBox;
foreach (var child in Children)
{

View File

@@ -61,7 +61,7 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var bg = _getBackground();
bg?.Draw(handle, SizeBox);
bg?.Draw(handle, PixelSizeBox);
var fg = _getForeground();
if (fg == null)
@@ -69,10 +69,10 @@ namespace Robust.Client.UserInterface.Controls
return;
}
var minSize = fg.MinimumSize;
var size = Width * GetAsRatio() - minSize.X;
var size = PixelWidth * GetAsRatio() - minSize.X;
if (size > 0)
{
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, Height));
fg.Draw(handle, UIBox2.FromDimensions(0, 0, minSize.X + size, PixelHeight));
}
}

View File

@@ -85,6 +85,12 @@ namespace Robust.Client.UserInterface.Controls
}
}
public virtual void SetValueWithoutEvent(float newValue)
{
newValue = ClampValue(newValue);
_value = newValue;
}
private void _ensureValueClamped()
{
var newValue = ClampValue(_value);

View File

@@ -51,7 +51,7 @@ namespace Robust.Client.UserInterface.Controls
{
var oldHeight = _entry.Height;
var oldWidth = _entry.Width;
_entry.Update(font, MaxWidth ?? Width, UIScale);
_entry.Update(font, (MaxWidth ?? Width) * UIScale, UIScale);
if (oldHeight != _entry.Height || MaxWidth != null && _entry.Width != oldWidth)
{
MinimumSizeChanged();

View File

@@ -24,6 +24,8 @@ namespace Robust.Client.UserInterface.Controls
private StyleBox? _fillStyleBoxOverride;
private StyleBox? _grabberStyleBoxOverride;
public bool Grabbed => _grabbed;
public StyleBox? ForegroundStyleBoxOverride
{
get => _foregroundStyleBoxOverride;
@@ -97,6 +99,12 @@ namespace Robust.Client.UserInterface.Controls
}
}
public override void SetValueWithoutEvent(float newValue)
{
base.SetValueWithoutEvent(newValue);
UpdateValue();
}
private void UpdateValue()
{
var ratio = GetAsRatio();
@@ -131,10 +139,9 @@ namespace Robust.Client.UserInterface.Controls
{
base.KeyBindUp(args);
if (args.Function == EngineKeyFunctions.UIClick)
{
_grabbed = false;
}
if (args.Function != EngineKeyFunctions.UIClick) return;
_grabbed = false;
}
protected internal override void MouseMove(GUIMouseMoveEventArgs args)

View File

@@ -9,6 +9,7 @@ namespace Robust.Client.UserInterface.Controls
/// <summary>
/// Simple control that draws a single texture using a variety of possible stretching modes.
/// </summary>
/// <seealso cref="AnimatedTextureRect"/>
public class TextureRect : Control
{
public const string StylePropertyTexture = "texture";
@@ -28,8 +29,13 @@ namespace Robust.Client.UserInterface.Controls
get => _texture;
set
{
var oldSize = _texture?.Size;
_texture = value;
MinimumSizeChanged();
if (value?.Size != oldSize)
{
MinimumSizeChanged();
}
}
}

View File

@@ -5,7 +5,6 @@ using System.IO;
using Newtonsoft.Json;
using Robust.Client.Console;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Console;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Resources;
@@ -15,15 +14,29 @@ using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.CustomControls
{
public interface IDebugConsoleView
{
/// <summary>
/// Write a line with a specific color to the console window.
/// </summary>
void AddLine(string text, Color color);
void AddLine(string text);
void AddFormattedLine(FormattedMessage message);
void Clear();
}
// Quick note on how thread safety works in here:
// Messages from other threads are not actually immediately drawn. They're stored in a queue.
// Every frame OR the next time a message on the main thread comes in, this queue is drained.
// This keeps thread safety while still making it so messages are ordered how they come in.
// And also if Update() stops firing due to an exception loop the console will still work.
// (At least from the main thread, which is what's throwing the exceptions..)
public class DebugConsole : Control, IDebugConsole
public class DebugConsole : Control, IDebugConsoleView
{
private readonly IClientConsole _console;
private readonly IClientConsoleHost _consoleHost;
private readonly IResourceManager _resourceManager;
private static readonly ResourcePath HistoryPath = new("/debug_console_history.json");
@@ -32,7 +45,6 @@ namespace Robust.Client.UserInterface.CustomControls
private readonly OutputPanel Output;
private readonly Control MainControl;
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _console.Commands;
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new();
private bool _targetVisible;
@@ -41,9 +53,9 @@ namespace Robust.Client.UserInterface.CustomControls
private readonly List<string> searchResults;
private int searchIndex = 0;
public DebugConsole(IClientConsole console, IResourceManager resMan)
public DebugConsole(IClientConsoleHost consoleHost, IResourceManager resMan)
{
_console = console;
_consoleHost = consoleHost;
_resourceManager = resMan;
Visible = false;
@@ -82,9 +94,9 @@ namespace Robust.Client.UserInterface.CustomControls
CommandBar.OnTextEntered += CommandEntered;
CommandBar.OnHistoryChanged += OnHistoryChanged;
_console.AddString += (_, args) => AddLine(args.Text, args.Color);
_console.AddFormatted += (_, args) => AddFormattedLine(args.Message);
_console.ClearText += (_, args) => Clear();
_consoleHost.AddString += (_, args) => AddLine(args.Text, args.Color);
_consoleHost.AddFormatted += (_, args) => AddFormattedLine(args.Message);
_consoleHost.ClearText += (_, args) => Clear();
_loadHistoryFromDisk();
@@ -141,9 +153,10 @@ namespace Robust.Client.UserInterface.CustomControls
{
if (!string.IsNullOrWhiteSpace(args.Text))
{
_console.ProcessCommand(args.Text);
_consoleHost.ExecuteCommand(args.Text);
CommandBar.Clear();
}
commandChanged = true;
}
@@ -211,7 +224,7 @@ namespace Robust.Client.UserInterface.CustomControls
NextCommand();
args.Handle();
}
else if(args.Function == EngineKeyFunctions.GuiTabNavigatePrev)
else if (args.Function == EngineKeyFunctions.GuiTabNavigatePrev)
{
PrevCommand();
args.Handle();
@@ -229,7 +242,7 @@ namespace Robust.Client.UserInterface.CustomControls
searchResults.Clear();
searchIndex = 0;
commandChanged = false;
foreach (var cmd in Commands)
foreach (var cmd in _consoleHost.RegisteredCommands)
{
if (cmd.Key.StartsWith(CommandBar.Text))
{
@@ -264,7 +277,7 @@ namespace Robust.Client.UserInterface.CustomControls
if (searchResults.Count == 0)
return;
searchIndex = MathHelper.Mod(searchIndex - 1, searchResults.Count);
searchIndex = MathHelper.Mod(searchIndex - 1, searchResults.Count);
SetInput(searchResults[searchIndex]);
return;
}

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
@@ -17,7 +17,7 @@ namespace Robust.Client.UserInterface
public string Help => "scene <className>";
public string Description => "Immediately changes the UI scene/state.";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var reflection = IoCManager.Resolve<IReflectionManager>();
var type = reflection.LooseGetType(args[0]);
@@ -25,8 +25,6 @@ namespace Robust.Client.UserInterface
var stateMan = IoCManager.Resolve<IStateManager>();
stateMan.RequestStateChange(type);
return false;
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Robust.Client.UserInterface
{
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IClientConsole _console = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -97,7 +97,7 @@ namespace Robust.Client.UserInterface
_initializeCommon();
DebugConsole = new DebugConsole(_console, _resourceManager);
DebugConsole = new DebugConsole(_consoleHost, _resourceManager);
RootControl.AddChild(DebugConsole);
_debugMonitors = new DebugMonitors(_gameTiming, _playerManager, _eyeManager, _inputManager, _stateManager,

View File

@@ -4,6 +4,7 @@ using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
@@ -11,66 +12,67 @@ using Robust.Shared.Utility;
namespace Robust.Client.Utility
{
/// <summary>
/// Helper methods for resolving <see cref="SpriteSpecifier"/>s.
/// </summary>
public static class SpriteSpecifierExt
{
public static Texture GetTexture(this SpriteSpecifier.Texture texSpecifier, IResourceCache cache)
{
return cache
.GetResource<TextureResource>(SharedSpriteComponent.TextureRoot / texSpecifier.TexturePath)
.Texture;
}
public static RSI.State GetState(this SpriteSpecifier.Rsi rsiSpecifier, IResourceCache cache)
{
if (cache.TryGetResource<RSIResource>(
SharedSpriteComponent.TextureRoot / rsiSpecifier.RsiPath,
out var theRsi))
{
if (theRsi.RSI.TryGetState(rsiSpecifier.RsiState, out var state))
{
return state;
}
}
Logger.Error("Failed to load RSI {0}", rsiSpecifier.RsiPath);
return SpriteComponent.GetFallbackState(cache);
}
public static Texture Frame0(this SpriteSpecifier specifier)
{
var resc = IoCManager.Resolve<IResourceCache>();
switch (specifier)
{
case SpriteSpecifier.Texture tex:
return resc.GetResource<TextureResource>(SpriteComponent.TextureRoot / tex.TexturePath).Texture;
case SpriteSpecifier.Rsi rsi:
if (resc.TryGetResource<RSIResource>(SpriteComponent.TextureRoot / rsi.RsiPath, out var theRsi))
{
if (theRsi.RSI.TryGetState(rsi.RsiState, out var state))
{
return state.Frame0;
}
}
Logger.Error("Failed to load RSI {0}", rsi.RsiPath);
return resc.GetFallback<TextureResource>().Texture;
case SpriteSpecifier.EntityPrototype prototypeIcon:
var protMgr = IoCManager.Resolve<IPrototypeManager>();
if (!protMgr.TryIndex<EntityPrototype>(prototypeIcon.EntityPrototypeId, out var prototype))
{
Logger.Error("Failed to load EntityPrototype for EntityPrototypeId {0}", prototypeIcon.EntityPrototypeId);
return resc.GetFallback<TextureResource>().Texture;
}
return SpriteComponent.GetPrototypeIcon(prototype, resc)?.Default ?? resc.GetFallback<TextureResource>().Texture;
default:
throw new NotImplementedException();
}
return specifier.RsiStateLike().Default;
}
public static IDirectionalTextureProvider DirFrame0(this SpriteSpecifier specifier)
{
var resc = IoCManager.Resolve<IResourceCache>();
return specifier.RsiStateLike();
}
public static IRsiStateLike RsiStateLike(this SpriteSpecifier specifier)
{
var resC = IoCManager.Resolve<IResourceCache>();
switch (specifier)
{
case SpriteSpecifier.Texture tex:
return resc.GetResource<TextureResource>(SpriteComponent.TextureRoot / tex.TexturePath).Texture;
return tex.GetTexture(resC);
case SpriteSpecifier.Rsi rsi:
if (resc.TryGetResource<RSIResource>(SpriteComponent.TextureRoot / rsi.RsiPath, out var theRsi))
{
if (theRsi.RSI.TryGetState(rsi.RsiState, out var state))
{
return state;
}
}
return resc.GetFallback<TextureResource>().Texture;
return rsi.GetState(resC);
case SpriteSpecifier.EntityPrototype prototypeIcon:
var protMgr = IoCManager.Resolve<IPrototypeManager>();
if (!protMgr.TryIndex<EntityPrototype>(prototypeIcon.EntityPrototypeId, out var prototype))
{
Logger.Error("Failed to load PrototypeIcon {0}", prototypeIcon.EntityPrototypeId);
return resc.GetFallback<TextureResource>().Texture;
return SpriteComponent.GetFallbackState(resC);
}
return SpriteComponent.GetPrototypeIcon(prototype, resc) ?? resc.GetFallback<TextureResource>().Texture;
return SpriteComponent.GetPrototypeIcon(prototype, resC);
default:
throw new NotImplementedException();
throw new NotSupportedException();
}
}
}

View File

@@ -1,10 +1,12 @@
using System;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Client.ViewVariables.Editors
{
@@ -13,48 +15,32 @@ namespace Robust.Client.ViewVariables.Editors
protected override Control MakeUI(object? value)
{
DebugTools.Assert(value!.GetType().IsEnum);
var enumVal = (Enum)value;
var enumType = value.GetType();
var enumStorageType = enumType.GetEnumUnderlyingType();
var enumList = Enum.GetValues(enumType);
var hBox = new HBoxContainer
var optionButton = new OptionButton();
foreach (var val in enumList)
{
CustomMinimumSize = new Vector2(200, 0)
};
var label = val?.ToString();
if (label == null)
continue;
optionButton.AddItem(label, Convert.ToInt32(val));
}
var lineEdit = new LineEdit
{
Text = enumVal.ToString(),
Editable = !ReadOnly,
SizeFlagsHorizontal = Control.SizeFlags.FillExpand
};
optionButton.SelectId(Convert.ToInt32(value));
optionButton.Disabled = ReadOnly;
if (!ReadOnly)
{
lineEdit.OnTextEntered += e =>
var underlyingType = Enum.GetUnderlyingType(value.GetType());
optionButton.OnItemSelected += e =>
{
var parseSig = new []{typeof(string), typeof(NumberStyles), typeof(CultureInfo), enumStorageType.MakeByRefType()};
var parseMethod = enumStorageType.GetMethod("TryParse", parseSig);
DebugTools.AssertNotNull(parseMethod);
var parameters = new object?[] {e.Text, NumberStyles.Integer, CultureInfo.InvariantCulture, null};
var parseWorked = (bool)parseMethod!.Invoke(null, parameters)!;
if (parseWorked) // textbox was the underlying type
{
DebugTools.AssertNotNull(parameters[3]);
ValueChanged(parameters[3]);
}
else if(Enum.TryParse(enumType, e.Text, true, out var enumValue))
{
var underlyingVal = Convert.ChangeType(enumValue, enumStorageType);
ValueChanged(underlyingVal);
}
optionButton.SelectId(e.Id);
ValueChanged(Convert.ChangeType(e.Id, underlyingType));
};
}
hBox.AddChild(lineEdit);
return hBox;
return optionButton;
}
}
}

View File

@@ -1,15 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Robust.Client.Console;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.ViewVariables.Traits;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -38,6 +44,9 @@ namespace Robust.Client.ViewVariables.Instances
private TabContainer _tabs = default!;
private IEntity _entity = default!;
private ViewVariablesAddComponentWindow? _addComponentWindow;
private bool _addComponentServer;
private ViewVariablesRemoteSession? _entitySession;
private ViewVariablesBlobMembers? _membersBlob;
@@ -47,6 +56,9 @@ namespace Robust.Client.ViewVariables.Instances
private VBoxContainer _serverVariables = default!;
private VBoxContainer _serverComponents = default!;
private Button _clientComponentsAddButton = default!;
private Button _serverComponentsAddButton = default!;
private LineEdit _clientComponentsSearchBar = default!;
private LineEdit _serverComponentsSearchBar = default!;
@@ -135,23 +147,7 @@ namespace Robust.Client.ViewVariables.Instances
_tabs.AddChild(_clientComponents);
_tabs.SetTabTitle(TabClientComponents, "Client Components");
_clientComponents.AddChild(_clientComponentsSearchBar = new LineEdit
{
PlaceHolder = Loc.GetString("Search"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_clientComponentsSearchBar.OnTextChanged += OnClientComponentsSearchBarChanged;
// See engine#636 for why the Distinct() call.
var componentList = _entity.GetAllComponents().OrderBy(c => c.GetType().ToString());
foreach (var component in componentList)
{
var button = new Button {Text = TypeAbbreviation.Abbreviate(component.GetType()), TextAlign = Label.AlignMode.Left};
button.OnPressed += args => { ViewVariablesManager.OpenVV(component); };
_clientComponents.AddChild(button);
}
PopulateClientComponents();
if (!_entity.Uid.IsClientSide())
{
@@ -163,13 +159,103 @@ namespace Robust.Client.ViewVariables.Instances
_tabs.AddChild(_serverComponents);
_tabs.SetTabTitle(TabServerComponents, "Server Components");
_serverComponents.AddChild(_serverComponentsSearchBar = new LineEdit
{
PlaceHolder = Loc.GetString("Search"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
PopulateServerComponents(false);
}
}
_serverComponentsSearchBar.OnTextChanged += OnServerComponentsSearchBarChanged;
private void PopulateClientComponents()
{
_clientComponents.DisposeAllChildren();
_clientComponents.AddChild(_clientComponentsSearchBar = new LineEdit
{
PlaceHolder = Loc.GetString("Search"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_clientComponents.AddChild(_clientComponentsAddButton = new Button()
{
Text = Loc.GetString("Add Component"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_clientComponentsAddButton.OnPressed += OnClientComponentsAddButtonPressed;
_clientComponentsSearchBar.OnTextChanged += OnClientComponentsSearchBarChanged;
var componentList = _entity.GetAllComponents().OrderBy(c => c.GetType().ToString());
foreach (var component in componentList)
{
var button = new Button {Text = TypeAbbreviation.Abbreviate(component.GetType()), TextAlign = Label.AlignMode.Left};
var removeButton = new TextureButton()
{
StyleClasses = { SS14Window.StyleClassWindowCloseButton },
SizeFlagsHorizontal = SizeFlags.ShrinkEnd
};
button.OnPressed += _ => ViewVariablesManager.OpenVV(component);
removeButton.OnPressed += _ => RemoveClientComponent(component);
button.AddChild(removeButton);
_clientComponents.AddChild(button);
}
}
private async void PopulateServerComponents(bool request = true)
{
_serverComponents.DisposeAllChildren();
_serverComponents.AddChild(_serverComponentsSearchBar = new LineEdit
{
PlaceHolder = Loc.GetString("Search"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_serverComponents.AddChild(_serverComponentsAddButton = new Button()
{
Text = Loc.GetString("Add Component"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_serverComponentsSearchBar.OnTextChanged += OnServerComponentsSearchBarChanged;
_serverComponentsAddButton.OnPressed += OnServerComponentsAddButtonPressed;
if (!request || _entitySession == null) return;
var componentsBlob = await ViewVariablesManager.RequestData<ViewVariablesBlobEntityComponents>(_entitySession, new ViewVariablesRequestEntityComponents());
componentsBlob.ComponentTypes.Sort();
var componentTypes = componentsBlob.ComponentTypes.AsEnumerable();
if (!string.IsNullOrEmpty(_serverComponentsSearchBar.Text))
{
componentTypes = componentTypes
.Where(t => t.Stringified.Contains(_serverComponentsSearchBar.Text,
StringComparison.InvariantCultureIgnoreCase));
}
componentTypes = componentTypes.OrderBy(t => t.Stringified);
foreach (var componentType in componentTypes)
{
var button = new Button {Text = componentType.Stringified, TextAlign = Label.AlignMode.Left};
var removeButton = new TextureButton()
{
StyleClasses = { SS14Window.StyleClassWindowCloseButton },
SizeFlagsHorizontal = SizeFlags.ShrinkEnd
};
button.OnPressed += _ =>
{
ViewVariablesManager.OpenVV(
new ViewVariablesComponentSelector(_entity.Uid, componentType.FullName));
};
removeButton.OnPressed += _ =>
{
// We send a command to remove the component.
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_entity.Uid} {componentType.ComponentName}");
PopulateServerComponents();
};
button.AddChild(removeButton);
_serverComponents.AddChild(button);
}
}
@@ -187,7 +273,7 @@ namespace Robust.Client.ViewVariables.Instances
foreach (var child in _clientComponents.Children)
{
if (child is not Button button)
if (child is not Button button || child == _clientComponentsAddButton)
{
continue;
}
@@ -222,7 +308,7 @@ namespace Robust.Client.ViewVariables.Instances
foreach (var child in _serverComponents.Children)
{
if (child is not Button button)
if (child is not Button button || child == _serverComponentsAddButton)
{
continue;
}
@@ -253,6 +339,103 @@ namespace Robust.Client.ViewVariables.Instances
UpdateServerComponentListVisibility(args.Text);
}
private void OnClientComponentsAddButtonPressed(BaseButton.ButtonEventArgs _)
{
_addComponentWindow?.Dispose();
_addComponentWindow = new ViewVariablesAddComponentWindow(GetValidComponentsForAdding(), false);
_addComponentWindow.AddComponentButtonPressed += OnTryAddComponent;
_addComponentServer = false;
_addComponentWindow.OpenCentered();
}
private async void OnServerComponentsAddButtonPressed(BaseButton.ButtonEventArgs _)
{
_addComponentWindow?.Dispose();
if (_entitySession == null) return;
_addComponentWindow = new ViewVariablesAddComponentWindow(await GetValidServerComponentsForAdding(), true);
_addComponentWindow.AddComponentButtonPressed += OnTryAddComponent;
_addComponentServer = true;
_addComponentWindow.OpenCentered();
}
/// <summary>
/// Returns an enumeration of components that can *probably* be added to an entity.
/// </summary>
private IEnumerable<string> GetValidComponentsForAdding()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
foreach (var type in componentFactory.AllRegisteredTypes)
{
if (_entity.HasComponent(type))
continue;
yield return (componentFactory.GetRegistration(type).Name);
}
}
/// <summary>
/// Requests and returns an enumeration of server-side components that can *probably* be added to an entity.
/// </summary>
private async Task<IEnumerable<string>> GetValidServerComponentsForAdding()
{
var blob = (ViewVariablesBlobAllValidComponents)
await ViewVariablesManager.RequestData(_entitySession!, new ViewVariablesRequestAllValidComponents());
return blob.ComponentTypes;
}
private async void OnTryAddComponent(ViewVariablesAddComponentWindow.AddComponentButtonPressedEventArgs eventArgs)
{
if (_addComponentServer)
{
// Attempted to add a component to the server entity... We send a command.
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_entity.Uid} {eventArgs.Component}");
PopulateServerComponents();
_addComponentWindow?.Populate(await GetValidServerComponentsForAdding());
return;
}
var componentFactory = IoCManager.Resolve<IComponentFactory>();
if(!componentFactory.TryGetRegistration(eventArgs.Component, out var registration)) return;
try
{
var comp = (Component) componentFactory.GetComponent(registration.Type);
comp.Owner = _entity;
_entityManager.ComponentManager.AddComponent(_entity, comp);
}
catch (Exception e)
{
Logger.Warning($"Failed to add component!\n{e}");
}
PopulateClientComponents();
// Update list of components.
_addComponentWindow?.Populate(GetValidComponentsForAdding());
}
private void RemoveClientComponent(IComponent component)
{
try
{
_entityManager.ComponentManager.RemoveComponent(_entity.Uid, component);
}
catch (Exception e)
{
Logger.Warning($"Couldn't remove component!\n{e}");
}
PopulateClientComponents();
}
public override async void Initialize(SS14Window window, ViewVariablesBlobMetadata blob, ViewVariablesRemoteSession session)
{
// TODO: this is pretty poorly implemented right now.
@@ -282,6 +465,12 @@ namespace Robust.Client.ViewVariables.Instances
private async void _tabsOnTabChanged(int tab)
{
if (tab == TabClientComponents)
{
// Repopulate client components in case something changed.
PopulateClientComponents();
}
if (_serverLoaded || tab != TabServerComponents && tab != TabServerVars)
{
return;
@@ -303,10 +492,10 @@ namespace Robust.Client.ViewVariables.Instances
_serverComponents.AddChild(new Label {Text = text});
return;
}
_membersBlob = await ViewVariablesManager.RequestData<ViewVariablesBlobMembers>(_entitySession, new ViewVariablesRequestMembers());
}
_membersBlob = await ViewVariablesManager.RequestData<ViewVariablesBlobMembers>(_entitySession, new ViewVariablesRequestMembers());
var otherStyle = false;
var first = true;
foreach (var (groupName, groupMembers) in _membersBlob!.MemberGroups)
@@ -326,41 +515,7 @@ namespace Robust.Client.ViewVariables.Instances
}
}
var componentsBlob = await ViewVariablesManager.RequestData<ViewVariablesBlobEntityComponents>(_entitySession, new ViewVariablesRequestEntityComponents());
_serverComponents.DisposeAllChildren();
_serverComponents.AddChild(_serverComponentsSearchBar = new LineEdit
{
PlaceHolder = Loc.GetString("Search"),
SizeFlagsHorizontal = SizeFlags.FillExpand
});
_serverComponentsSearchBar.OnTextChanged += OnServerComponentsSearchBarChanged;
componentsBlob.ComponentTypes.Sort();
var componentTypes = componentsBlob.ComponentTypes.AsEnumerable();
if (!string.IsNullOrEmpty(_serverComponentsSearchBar.Text))
{
componentTypes = componentTypes
.Where(t => t.Stringified.Contains(_serverComponentsSearchBar.Text,
StringComparison.InvariantCultureIgnoreCase));
}
componentTypes = componentTypes.OrderBy(t => t.Stringified);
foreach (var componentType in componentTypes)
{
var button = new Button {Text = componentType.Stringified, TextAlign = Label.AlignMode.Left};
button.OnPressed += args =>
{
ViewVariablesManager.OpenVV(
new ViewVariablesComponentSelector(_entity.Uid, componentType.FullName));
};
_serverComponents.AddChild(button);
}
PopulateServerComponents();
}
}
}

View File

@@ -0,0 +1,8 @@
<cc:SS14Window xmlns:cc="clr-namespace:Robust.Client.UserInterface.CustomControls"
xmlns:c="clr-namespace:Robust.Client.UserInterface.Controls">
<c:VBoxContainer SizeFlagsVertical="FillExpand">
<c:LineEdit Name="SearchLineEdit" PlaceHolder="Search component..." SizeFlagsHorizontal="FillExpand" />
<c:ItemList Name="ComponentItemList" SizeFlagsVertical="FillExpand" SizeFlagsHorizontal="FillExpand" SelectMode="Single" />
<c:Button Name="AddButton" Text="Add Component" TextAlign="Center" SizeFlagsHorizontal="FillExpand"/>
</c:VBoxContainer>
</cc:SS14Window>

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Robust.Client.ViewVariables
{
[GenerateTypedNameReferences]
public partial class ViewVariablesAddComponentWindow : SS14Window
{
protected override Vector2? CustomSize => (200f, 300f);
private string? _lastSearch;
private string[] _components = Array.Empty<string>();
public event Action<AddComponentButtonPressedEventArgs>? AddComponentButtonPressed;
public ViewVariablesAddComponentWindow(IEnumerable<string> components, bool serverside)
{
RobustXamlLoader.Load(this);
Title = Loc.GetString(serverside ? "Add Component [S]" : "Add Component [C]");
ComponentItemList.OnItemSelected += _ => RefreshAddButton();
ComponentItemList.OnItemDeselected += _ => RefreshAddButton();
SearchLineEdit.OnTextChanged += OnSearchTextChanged;
AddButton.OnPressed += OnAddButtonPressed;
Populate(components);
}
private void RefreshAddButton()
{
AddButton.Disabled = !ComponentItemList.GetSelected().Any();
}
public void Populate(IEnumerable<string> components)
{
_components = components.ToArray();
Array.Sort(_components);
Populate(_lastSearch);
}
private void Populate(string? search = null)
{
_lastSearch = search;
ComponentItemList.ClearSelected();
ComponentItemList.Clear();
AddButton.Disabled = true;
foreach (var component in _components)
{
if(!string.IsNullOrEmpty(search) && !component.Contains(search, StringComparison.InvariantCultureIgnoreCase))
continue;
ComponentItemList.AddItem(component);
}
}
private void OnSearchTextChanged(LineEdit.LineEditEventArgs obj)
{
Populate(obj.Text);
}
private void OnAddButtonPressed(BaseButton.ButtonEventArgs obj)
{
var selected = ComponentItemList.GetSelected().ToArray();
// Nothing to do here!
if (selected.Length == 0)
return;
var comp = selected[0];
// This shouldn't really happen.
if (comp.Text == null)
return;
AddComponentButtonPressed?.Invoke(new AddComponentButtonPressedEventArgs(comp.Text));
}
public class AddComponentButtonPressedEventArgs : EventArgs
{
public string Component { get; }
public AddComponentButtonPressedEventArgs(string component)
{
Component = component;
}
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.UserInterface;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
@@ -20,7 +20,7 @@ namespace Robust.Client.ViewVariables
public string Description => "Opens View Variables.";
public string Help => "Usage: vv <entity ID|IoC interface name|SIoC interface name>";
public bool Execute(IDebugConsole console, params string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var vvm = IoCManager.Resolve<IViewVariablesManager>();
// If you don't provide an entity ID, it opens the test class.
@@ -28,7 +28,7 @@ namespace Robust.Client.ViewVariables
if (args.Length == 0)
{
vvm.OpenVV(new VVTest());
return false;
return;
}
var valArg = args[0];
@@ -37,7 +37,7 @@ namespace Robust.Client.ViewVariables
// Server-side IoC selector.
var selector = new ViewVariablesIoCSelector(valArg.Substring(1));
vvm.OpenVV(selector);
return false;
return;
}
if (valArg.StartsWith("I"))
@@ -46,8 +46,8 @@ namespace Robust.Client.ViewVariables
var reflection = IoCManager.Resolve<IReflectionManager>();
if (!reflection.TryLooseGetType(valArg, out var type))
{
console.AddLine("Unable to find that type.");
return false;
shell.WriteLine("Unable to find that type.");
return;
}
object obj;
@@ -57,11 +57,11 @@ namespace Robust.Client.ViewVariables
}
catch (UnregisteredTypeException)
{
console.AddLine("Unable to find that type.");
return false;
shell.WriteLine("Unable to find that type.");
return;
}
vvm.OpenVV(obj);
return false;
return;
}
if (valArg.StartsWith("guihover"))
@@ -70,30 +70,28 @@ namespace Robust.Client.ViewVariables
var obj = IoCManager.Resolve<IUserInterfaceManager>().CurrentlyHovered;
if (obj == null)
{
console.AddLine("Not currently hovering any control.");
return false;
shell.WriteLine("Not currently hovering any control.");
return;
}
vvm.OpenVV(obj);
return false;
return;
}
// Entity.
if (!EntityUid.TryParse(args[0], out var uid))
{
console.AddLine("Invalid specifier format.");
return false;
shell.WriteLine("Invalid specifier format.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetEntity(uid, out var entity))
{
console.AddLine("That entity does not exist.");
return false;
shell.WriteLine("That entity does not exist.");
return;
}
vvm.OpenVV(entity);
return false;
}
/// <summary>

View File

@@ -6,7 +6,6 @@ using System.Threading;
using Prometheus;
using Robust.Server.Console;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.GameState;
using Robust.Server.Interfaces.Placement;
@@ -238,7 +237,6 @@ namespace Robust.Server
{
netMan.Initialize(true);
netMan.StartServer();
netMan.RegisterNetMessage<MsgSetTickRate>(MsgSetTickRate.NAME);
}
catch (Exception e)
{
@@ -310,7 +308,7 @@ namespace Robust.Server
prototypeManager.LoadDirectory(new ResourcePath(@"/Prototypes"));
prototypeManager.Resync();
IoCManager.Resolve<IConsoleShell>().Initialize();
IoCManager.Resolve<IServerConsoleHost>().Initialize();
_entities.Startup();
_scriptHost.Initialize();
@@ -468,7 +466,6 @@ namespace Robust.Server
_time.TickRate = b;
Logger.InfoS("game", $"Tickrate changed to: {b} on tick {_time.CurTick}");
SendTickRateUpdateToClients(b);
});
_time.TickRate = (byte) _config.GetCVar(CVars.NetTickrate);
@@ -478,14 +475,6 @@ namespace Robust.Server
Logger.InfoS("srv", $"Max players: {MaxPlayers}");
}
private void SendTickRateUpdateToClients(byte newTickRate)
{
var msg = _network.CreateNetMessage<MsgSetTickRate>();
msg.NewTickRate = newTickRate;
_network.ServerSendToAll(msg);
}
// called right before main loop returns, do all saving/cleanup in here
private void Cleanup()
{

View File

@@ -1,6 +1,6 @@
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -8,17 +8,17 @@ using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal sealed class AddCompCommand : IClientCommand
internal sealed class AddCompCommand : IConsoleCommand
{
public string Command => "addcomp";
public string Description => "Adds a component to an entity";
public string Help => "addcomp <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}
@@ -39,16 +39,16 @@ namespace Robust.Server.Console.Commands
}
[UsedImplicitly]
internal sealed class RemoveCompCommand : IClientCommand
internal sealed class RemoveCompCommand : IConsoleCommand
{
public string Command => "rmcomp";
public string Description => "Removes a component from an entity.";
public string Help => "rmcomp <uid> <componentName>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Wrong number of arguments");
shell.WriteLine("Wrong number of arguments");
return;
}

View File

@@ -1,21 +1,21 @@
using System;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal sealed class CVarCommand : SharedCVarCommand, IClientCommand
internal sealed class CVarCommand : SharedCVarCommand, IConsoleCommand
{
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
shell.SendText(player, "Must provide exactly one or two arguments.");
shell.WriteLine("Must provide exactly one or two arguments.");
return;
}
@@ -25,13 +25,13 @@ namespace Robust.Server.Console.Commands
if (name == "?")
{
var cvars = configManager.GetRegisteredCVars();
shell.SendText(player, string.Join("\n", cvars));
shell.WriteLine(string.Join("\n", cvars));
return;
}
if (!configManager.IsCVarRegistered(name))
{
shell.SendText(player, $"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
shell.WriteLine($"CVar '{name}' is not registered. Use 'cvar ?' to get a list of all registered CVars.");
return;
}
@@ -39,7 +39,7 @@ namespace Robust.Server.Console.Commands
{
// Read CVar
var value = configManager.GetCVar<object>(name);
shell.SendText(player, value.ToString());
shell.WriteLine(value.ToString()!);
}
else
{
@@ -53,7 +53,7 @@ namespace Robust.Server.Console.Commands
}
catch (FormatException)
{
shell.SendText(player, $"Input value is in incorrect format for type {type}");
shell.WriteLine($"Input value is in incorrect format for type {type}");
}
}
}

View File

@@ -1,22 +1,22 @@
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
public sealed class DeleteCommand : IClientCommand
public sealed class DeleteCommand : IConsoleCommand
{
public string Command => "delete";
public string Description => "Deletes the entity with the specified ID.";
public string Help => "delete <entity UID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "You should provide exactly one argument.");
shell.WriteLine("You should provide exactly one argument.");
return;
}
@@ -24,13 +24,13 @@ namespace Robust.Server.Console.Commands
if (!EntityUid.TryParse(args[0], out var uid))
{
shell.SendText(player, "Invalid entity UID.");
shell.WriteLine("Invalid entity UID.");
return;
}
if (!ent.TryGetEntity(uid, out var entity))
{
shell.SendText(player, "That entity does not exist.");
shell.WriteLine("That entity does not exist.");
return;
}

View File

@@ -1,19 +1,19 @@
using System.Runtime.Loader;
using System.Text;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Console.Commands
{
[UsedImplicitly]
internal class ListAssembliesCommand : IClientCommand
internal class ListAssembliesCommand : IConsoleCommand
{
public string Command => "lsasm";
public string Description => "Lists loaded assemblies by load context.";
public string Help => Command;
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var sb = new StringBuilder();
foreach (var context in AssemblyLoadContext.All)
@@ -25,7 +25,7 @@ namespace Robust.Server.Console.Commands
}
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
}

View File

@@ -1,11 +1,11 @@
using System.Linq;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Console.Commands
{
public class ListCommands : IClientCommand
public class ListCommands : IConsoleCommand
{
public string Command => "list";
@@ -15,7 +15,7 @@ namespace Robust.Server.Console.Commands
public string Help => "Usage: list [filter]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var filter = "";
if (args.Length == 1)
@@ -24,7 +24,7 @@ namespace Robust.Server.Console.Commands
}
var builder = new StringBuilder("SIDE NAME DESC\n-------------------------\n");
foreach (var command in shell.AvailableCommands.Values
foreach (var command in shell.ConsoleHost.RegisteredCommands.Values
.Where(p => p.Command.Contains(filter))
.OrderBy(c => c.Command))
{
@@ -34,7 +34,7 @@ namespace Robust.Server.Console.Commands
}
var message = builder.ToString().Trim(' ', '\n');
shell.SendText(player, message);
shell.WriteLine(message);
}
}
}

View File

@@ -1,11 +1,11 @@
using System;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Log;
namespace Robust.Server.Console.Commands
{
class LogSetLevelCommand : IClientCommand
class LogSetLevelCommand : IConsoleCommand
{
public string Command => "loglevel";
public string Description => "Changes the log level for a provided sawmill.";
@@ -13,11 +13,11 @@ namespace Robust.Server.Console.Commands
+ "\n sawmill: A label prefixing log messages. This is the one you're setting the level for."
+ "\n level: The log level. Must match one of the values of the LogLevel enum.";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.SendText(player, "Invalid argument amount. Expected 2 arguments.");
shell.WriteLine("Invalid argument amount. Expected 2 arguments.");
return;
}
@@ -32,7 +32,7 @@ namespace Robust.Server.Console.Commands
{
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
shell.SendText(player, "Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
level = result;
@@ -41,7 +41,7 @@ namespace Robust.Server.Console.Commands
}
}
class TestLog : IClientCommand
class TestLog : IConsoleCommand
{
public string Command => "testlog";
public string Description => "Writes a test log to a sawmill.";
@@ -50,11 +50,11 @@ namespace Robust.Server.Console.Commands
+ "\n level: The log level. Must match one of the values of the LogLevel enum."
+ "\n message: The message to be logged. Wrap this in double quotes if you want to use spaces.";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
shell.SendText(player, "Invalid argument amount. Expected exactly 3 arguments.");
shell.WriteLine("Invalid argument amount. Expected exactly 3 arguments.");
return;
}
@@ -63,7 +63,7 @@ namespace Robust.Server.Console.Commands
var message = args[2]; // yes this doesn't support spaces idgaf.
if (!Enum.TryParse<LogLevel>(levelname, out var result))
{
shell.SendText(player, "Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
shell.WriteLine("Failed to parse 2nd argument. Must be one of the values of the LogLevel enum.");
return;
}
var level = result;

View File

@@ -1,25 +1,27 @@
using System.Globalization;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Maps;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Timing;
using Robust.Server.Maps;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.Server.Console.Commands
{
class AddMapCommand : IClientCommand
class AddMapCommand : IConsoleCommand
{
public string Command => "addmap";
public string Description => "Adds a new empty map to the round. If the mapID already exists, this command does nothing.";
public string Help => "addmap <mapID> [initialize]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -36,24 +38,24 @@ namespace Robust.Server.Console.Commands
{
pauseMgr.AddUninitializedMap(mapId);
}
shell.SendText(player, $"Map with ID {mapId} created.");
shell.WriteLine($"Map with ID {mapId} created.");
return;
}
shell.SendText(player, $"Map with ID {mapId} already exists!");
shell.WriteLine($"Map with ID {mapId} already exists!");
}
}
class RemoveMapCommand : IClientCommand
class RemoveMapCommand : IConsoleCommand
{
public string Command => "rmmap";
public string Description => "Removes a map from the world. You cannot remove nullspace.";
public string Help => "rmmap <mapId>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -62,32 +64,32 @@ namespace Robust.Server.Console.Commands
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, $"Map {mapId.Value} does not exist.");
shell.WriteLine($"Map {mapId.Value} does not exist.");
return;
}
mapManager.DeleteMap(mapId);
shell.SendText(player, $"Map {mapId.Value} was removed.");
shell.WriteLine($"Map {mapId.Value} was removed.");
}
}
public class SaveBp : IClientCommand
public class SaveBp : IConsoleCommand
{
public string Command => "savebp";
public string Description => "Serializes a grid to disk.";
public string Help => "savebp <gridID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
shell.SendText(player, "Not enough arguments.");
shell.WriteLine("Not enough arguments.");
return;
}
if (!int.TryParse(args[0], out var intGridId))
{
shell.SendText(player, "Not a valid grid ID.");
shell.WriteLine("Not a valid grid ID.");
return;
}
@@ -98,22 +100,22 @@ namespace Robust.Server.Console.Commands
// no saving default grid
if (!mapManager.TryGetGrid(gridId, out var grid))
{
shell.SendText(player, "That grid does not exist.");
shell.WriteLine("That grid does not exist.");
return;
}
IoCManager.Resolve<IMapLoader>().SaveBlueprint(gridId, args[1]);
shell.SendText(player, "Save successful. Look in the user data directory.");
shell.WriteLine("Save successful. Look in the user data directory.");
}
}
public class LoadBp : IClientCommand
public class LoadBp : IConsoleCommand
{
public string Command => "loadbp";
public string Description => "Loads a blueprint from disk into the game.";
public string Help => "loadbp <MapID> <Path>";
public string Help => "loadbp <MapID> <Path> [storeUids]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
@@ -130,29 +132,35 @@ namespace Robust.Server.Console.Commands
// no loading into null space
if (mapId == MapId.Nullspace)
{
shell.SendText(player, "Cannot load into nullspace.");
shell.WriteLine("Cannot load into nullspace.");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "Target map does not exist.");
shell.WriteLine("Target map does not exist.");
return;
}
var loadOptions = new MapLoadOptions();
if (args.Length > 2)
{
loadOptions.StoreMapUids = bool.Parse(args[2]);
}
var mapLoader = IoCManager.Resolve<IMapLoader>();
mapLoader.LoadBlueprint(mapId, args[1]);
mapLoader.LoadBlueprint(mapId, args[1], loadOptions);
}
}
public class SaveMap : IClientCommand
public class SaveMap : IConsoleCommand
{
public string Command => "savemap";
public string Description => "Serializes a map to disk.";
public string Help => "savemap <MapID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -175,13 +183,13 @@ namespace Robust.Server.Console.Commands
}
}
public class LoadMap : IClientCommand
public class LoadMap : IConsoleCommand
{
public string Command => "loadmap";
public string Description => "Loads a map from disk into the game.";
public string Help => "loadmap <MapID> <Path>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
return;
@@ -194,50 +202,56 @@ namespace Robust.Server.Console.Commands
// no loading null space
if (mapID == MapId.Nullspace)
{
shell.SendText(player, "You cannot load into map 0.");
shell.WriteLine("You cannot load into map 0.");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
if (mapManager.MapExists(mapID))
{
shell.SendText(player, $"Map {mapID} already exists.");
shell.WriteLine($"Map {mapID} already exists.");
return;
}
// TODO: Parse path
var mapPath = "Maps/Demo/DemoMap.yaml";
IoCManager.Resolve<IMapLoader>().LoadMap(mapID, mapPath);
shell.SendText(player, $"Map {mapID} has been loaded from {mapPath}.");
shell.WriteLine($"Map {mapID} has been loaded from {mapPath}.");
}
}
class LocationCommand : IClientCommand
class LocationCommand : IConsoleCommand
{
public string Command => "loc";
public string Description => "Prints the absolute location of the player's entity to console.";
public string Help => "loc";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if(player?.AttachedEntity == null)
var player = shell.Player as IPlayerSession;
if (player?.AttachedEntity == null)
return;
var pos = player.AttachedEntity.Transform.Coordinates;
var entityManager = IoCManager.Resolve<IEntityManager>();
shell.SendText(player,
$"MapID:{pos.GetMapId(entityManager)} GridID:{pos.GetGridId(entityManager)} X:{pos.X:N2} Y:{pos.Y:N2}");
shell.WriteLine($"MapID:{pos.GetMapId(entityManager)} GridID:{pos.GetGridId(entityManager)} X:{pos.X:N2} Y:{pos.Y:N2}");
}
}
class PauseMapCommand : IClientCommand
class PauseMapCommand : IConsoleCommand
{
public string Command => "pausemap";
public string Description => "Pauses a map, pausing all simulation processing on it.";
public string Help => "Usage: pausemap <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine(Loc.GetString("Need to supply a valid MapId"));
return;
}
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -245,20 +259,26 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
pauseManager.SetMapPaused(mapId, true);
}
}
class UnpauseMapCommand : IClientCommand
class UnpauseMapCommand : IConsoleCommand
{
public string Command => "unpausemap";
public string Description => "unpauses a map, resuming all simulation processing on it.";
public string Help => "Usage: unpausemap <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine(Loc.GetString("Need to supply a valid MapId"));
return;
}
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -266,19 +286,19 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
pauseManager.SetMapPaused(mapId, false);
}
}
class QueryMapPausedCommand : IClientCommand
class QueryMapPausedCommand : IConsoleCommand
{
public string Command => "querymappaused";
public string Description => "Check whether a map is paused or not.";
public string Help => "Usage: querymappaused <map ID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var arg = args[0];
var mapId = new MapId(int.Parse(arg, CultureInfo.InvariantCulture));
@@ -287,24 +307,24 @@ namespace Robust.Server.Console.Commands
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "That map does not exist.");
shell.WriteLine("That map does not exist.");
return;
}
shell.SendText(player, pauseManager.IsMapPaused(mapId).ToString());
shell.WriteLine(pauseManager.IsMapPaused(mapId).ToString());
}
}
class TpGridCommand : IClientCommand
class TpGridCommand : IConsoleCommand
{
public string Command => "tpgrid";
public string Description => "Teleports a grid to a new location.";
public string Help => "tpgrid <gridId> <X> <Y> [<MapId>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 3 || args.Length > 4)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
}
var gridId = new GridId(int.Parse(args[0]));
@@ -320,21 +340,21 @@ namespace Robust.Server.Console.Commands
grid.ParentMapId = mapId;
grid.WorldPosition = new Vector2(xpos, ypos);
shell.SendText(player, "Grid was teleported.");
shell.WriteLine("Grid was teleported.");
}
}
}
class RemoveGridCommand : IClientCommand
class RemoveGridCommand : IConsoleCommand
{
public string Command => "rmgrid";
public string Description => "Removes a grid from a map. You cannot remove the default grid.";
public string Help => "rmgrid <gridId>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -343,26 +363,26 @@ namespace Robust.Server.Console.Commands
if (!mapManager.GridExists(gridId))
{
shell.SendText(player, $"Grid {gridId.Value} does not exist.");
shell.WriteLine($"Grid {gridId.Value} does not exist.");
return;
}
mapManager.DeleteGrid(gridId);
shell.SendText(player, $"Grid {gridId.Value} was removed.");
shell.WriteLine($"Grid {gridId.Value} was removed.");
}
}
internal sealed class RunMapInitCommand : IClientCommand
internal sealed class RunMapInitCommand : IConsoleCommand
{
public string Command => "mapinit";
public string Description => "Runs map init on a map";
public string Help => "mapinit <mapID>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.SendText(player, "Wrong number of args.");
shell.WriteLine("Wrong number of args.");
return;
}
@@ -374,13 +394,13 @@ namespace Robust.Server.Console.Commands
if (!mapManager.MapExists(mapId))
{
shell.SendText(player, "Map does not exist!");
shell.WriteLine("Map does not exist!");
return;
}
if (pauseManager.IsMapInitialized(mapId))
{
shell.SendText(player, "Map is already initialized!");
shell.WriteLine("Map is already initialized!");
return;
}
@@ -388,13 +408,13 @@ namespace Robust.Server.Console.Commands
}
}
internal sealed class ListMapsCommand : IClientCommand
internal sealed class ListMapsCommand : IConsoleCommand
{
public string Command => "lsmap";
public string Description => "Lists maps";
public string Help => "lsmap";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapManager = IoCManager.Resolve<IMapManager>();
var pauseManager = IoCManager.Resolve<IPauseManager>();
@@ -410,17 +430,17 @@ namespace Robust.Server.Console.Commands
mapManager.GetMapEntityId(mapId));
}
shell.SendText(player, msg.ToString());
shell.WriteLine(msg.ToString());
}
}
internal sealed class ListGridsCommand : IClientCommand
internal sealed class ListGridsCommand : IConsoleCommand
{
public string Command => "lsgrid";
public string Description => "List grids";
public string Help => "lsgrid";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var mapManager = IoCManager.Resolve<IMapManager>();
@@ -432,7 +452,7 @@ namespace Robust.Server.Console.Commands
grid.Index, grid.ParentMapId, grid.WorldPosition, grid.GridEntityId);
}
shell.SendText(player, msg.ToString());
shell.WriteLine(msg.ToString());
}
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System;
using System.Linq;
using System.Text;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
@@ -14,14 +14,15 @@ using Robust.Shared.Network;
namespace Robust.Server.Console.Commands
{
internal class TeleportCommand : IClientCommand
internal class TeleportCommand : IConsoleCommand
{
public string Command => "tp";
public string Description => "Teleports a player to any location in the round.";
public string Help => "tp <x> <y> [<mapID>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
if (player?.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
@@ -58,18 +59,19 @@ namespace Robust.Server.Console.Commands
transform.WorldPosition = position;
}
shell.SendText(player, $"Teleported {player} to {mapId}:{posX},{posY}.");
shell.WriteLine($"Teleported {player} to {mapId}:{posX},{posY}.");
}
}
public class TeleportToPlayerCommand : IClientCommand
public class TeleportToPlayerCommand : IConsoleCommand
{
public string Command => "tpto";
public string Description => "Teleports the current player to the location of another player.";
public string Help => "tpto <username>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
if (player?.Status != SessionStatus.InGame || player.AttachedEntity == null)
return;
@@ -89,13 +91,13 @@ namespace Robust.Server.Console.Commands
}
}
public class ListPlayers : IClientCommand
public class ListPlayers : IConsoleCommand
{
public string Command => "listplayers";
public string Description => "Lists all players currently connected";
public string Help => "listplayers";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// Player: number of people connected and their byond keys
// Admin: read a byond variable which shows their ip, byond version, ckey, attached entity and hardware id
@@ -117,29 +119,29 @@ namespace Robust.Server.Console.Commands
p.Name));
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
internal class KickCommand : IClientCommand
internal class KickCommand : IConsoleCommand
{
public string Command => "kick";
public string Description => "Kicks a connected player out of the server, disconnecting them.";
public string Help => "kick <PlayerIndex> [<Reason>]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var players = IoCManager.Resolve<IPlayerManager>();
if (args.Length < 1)
{
var player = shell.Player as IPlayerSession;
var toKickPlayer = player ?? players.GetAllPlayers().FirstOrDefault();
if (toKickPlayer == null)
{
shell.SendText(player, "You need to provide a player to kick.");
shell.WriteLine("You need to provide a player to kick.");
return;
}
shell.SendText(player,
$"You need to provide a player to kick. Try running 'kick {toKickPlayer?.Name}' as an example.");
shell.WriteLine($"You need to provide a player to kick. Try running 'kick {toKickPlayer?.Name}' as an example.");
return;
}

View File

@@ -1,19 +1,20 @@
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.IoC;
namespace Robust.Server.Console.Commands
{
public class SpawnCommand : IClientCommand
public class SpawnCommand : IConsoleCommand
{
public string Command => "spawn";
public string Description => "Spawns an entity with specific type at your feet.";
public string Help => "spawn <entity type>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player as IPlayerSession;
var ent = IoCManager.Resolve<IServerEntityManager>();
if (player?.AttachedEntity != null)
{

View File

@@ -1,9 +1,9 @@
using System;
using System;
using System.Runtime;
using System.Text;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
@@ -13,49 +13,49 @@ using Robust.Shared.Serialization;
namespace Robust.Server.Console.Commands
{
class RestartCommand : IClientCommand
class RestartCommand : IConsoleCommand
{
public string Command => "restart";
public string Description => "Gracefully restarts the server (not just the round).";
public string Help => "restart";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IBaseServer>().Restart();
}
}
class ShutdownCommand : IClientCommand
class ShutdownCommand : IConsoleCommand
{
public string Command => "shutdown";
public string Description => "Gracefully shuts down the server.";
public string Help => "shutdown";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IBaseServer>().Shutdown(null);
}
}
public class SaveConfig : IClientCommand
public class SaveConfig : IConsoleCommand
{
public string Command => "saveconfig";
public string Description => "Saves the server configuration to the config file";
public string Help => "saveconfig";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
IoCManager.Resolve<IConfigurationManager>().SaveToFile();
}
}
class NetworkAuditCommand : IClientCommand
class NetworkAuditCommand : IConsoleCommand
{
public string Command => "netaudit";
public string Description => "Prints into about NetMsg security.";
public string Help => "netaudit";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var network = (NetManager) IoCManager.Resolve<INetManager>();
@@ -71,11 +71,11 @@ namespace Robust.Server.Console.Commands
sb.AppendLine($"Type: {msgType.Name.PadRight(16)} Call:{call.Target}");
}
shell.SendText(player, sb.ToString());
shell.WriteLine(sb.ToString());
}
}
class HelpCommand : IClientCommand
class HelpCommand : IConsoleCommand
{
public string Command => "help";
@@ -84,54 +84,52 @@ namespace Robust.Server.Console.Commands
public string Help => "Help";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
switch (args.Length)
{
case 0:
shell.SendText(player,
"To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
shell.WriteLine("To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'.");
break;
case 1:
var commandName = args[0];
if (!shell.AvailableCommands.TryGetValue(commandName, out var cmd))
if (!shell.ConsoleHost.RegisteredCommands.TryGetValue(commandName, out var cmd))
{
shell.SendText(player, $"Unknown command: {commandName}");
shell.WriteLine($"Unknown command: {commandName}");
return;
}
shell.SendText(player, $"Use: {cmd.Help}\n{cmd.Description}");
shell.WriteLine($"Use: {cmd.Help}\n{cmd.Description}");
break;
default:
shell.SendText(player, "Invalid amount of arguments.");
shell.WriteLine("Invalid amount of arguments.");
break;
}
}
}
class ShowTimeCommand : IClientCommand
class ShowTimeCommand : IConsoleCommand
{
public string Command => "showtime";
public string Description => "Shows the server time.";
public string Help => "showtime";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var timing = IoCManager.Resolve<IGameTiming>();
shell.SendText(player,
$"Paused: {timing.Paused}, CurTick: {timing.CurTick}, CurTime: {timing.CurTime}, RealTime: {timing.RealTime}");
shell.WriteLine($"Paused: {timing.Paused}, CurTick: {timing.CurTick}, CurTime: {timing.CurTime}, RealTime: {timing.RealTime}");
}
}
internal class GcCommand : IClientCommand
internal class GcCommand : IConsoleCommand
{
public string Command => "gc";
public string Description => "Run the GC.";
public string Help => "gc [generation]";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
@@ -142,12 +140,12 @@ namespace Robust.Server.Console.Commands
if(int.TryParse(args[0], out int result))
GC.Collect(result);
else
shell.SendText(player, "Failed to parse argument.");
shell.WriteLine("Failed to parse argument.");
}
}
}
internal class GcModeCommand : IClientCommand
internal class GcModeCommand : IConsoleCommand
{
public string Command => "gc_mode";
@@ -156,16 +154,16 @@ namespace Robust.Server.Console.Commands
public string Help => "gc_mode [type]";
public void Execute(IConsoleShell console, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell console, string argStr, string[] args)
{
var prevMode = GCSettings.LatencyMode;
if (args.Length == 0)
{
console.SendText(player,$"current gc latency mode: {(int) prevMode} ({prevMode})");
console.SendText(player,"possible modes:");
console.WriteLine($"current gc latency mode: {(int) prevMode} ({prevMode})");
console.WriteLine("possible modes:");
foreach (var mode in (int[]) Enum.GetValues(typeof(GCLatencyMode)))
{
console.SendText(player,$" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
console.WriteLine($" {mode}: {Enum.GetName(typeof(GCLatencyMode), mode)}");
}
}
else
@@ -177,13 +175,13 @@ namespace Robust.Server.Console.Commands
}
else if (!Enum.TryParse(args[0], true, out mode))
{
console.SendText(player,$"unknown gc latency mode: {args[0]}");
console.WriteLine($"unknown gc latency mode: {args[0]}");
return;
}
console.SendText(player,$"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
console.WriteLine($"attempting gc latency mode change: {(int) prevMode} ({prevMode}) -> {(int) mode} ({mode})");
GCSettings.LatencyMode = mode;
console.SendText(player,$"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
console.WriteLine($"resulting gc latency mode: {(int) GCSettings.LatencyMode} ({GCSettings.LatencyMode})");
}
return;
@@ -191,7 +189,7 @@ namespace Robust.Server.Console.Commands
}
internal class SerializeStatsCommand : IClientCommand
internal class SerializeStatsCommand : IConsoleCommand
{
public string Command => "szr_stats";
@@ -200,31 +198,31 @@ namespace Robust.Server.Console.Commands
public string Help => "szr_stats";
public void Execute(IConsoleShell console, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell console, string argStr, string[] args)
{
console.SendText(player,$"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.SendText(player,$"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.SendText(player,$"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.SendText(player,$"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
console.WriteLine($"serialized: {RobustSerializer.BytesSerialized} bytes, {RobustSerializer.ObjectsSerialized} objects");
console.WriteLine($"largest serialized: {RobustSerializer.LargestObjectSerializedBytes} bytes, {RobustSerializer.LargestObjectSerializedType} objects");
console.WriteLine($"deserialized: {RobustSerializer.BytesDeserialized} bytes, {RobustSerializer.ObjectsDeserialized} objects");
console.WriteLine($"largest serialized: {RobustSerializer.LargestObjectDeserializedBytes} bytes, {RobustSerializer.LargestObjectDeserializedType} objects");
}
}
internal sealed class MemCommand : IClientCommand
internal sealed class MemCommand : IConsoleCommand
{
public string Command => "mem";
public string Description => "prints memory info";
public string Help => "mem";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
#if !NETCOREAPP
shell.SendText(player, "Memory info is only available on .NET Core");
#else
var info = GC.GetGCMemoryInfo();
shell.SendText(player, $@"Heap Size: {FormatBytes(info.HeapSizeBytes)} Total Allocated: {FormatBytes(GC.GetTotalMemory(false))}");
shell.WriteLine($@"Heap Size: {FormatBytes(info.HeapSizeBytes)} Total Allocated: {FormatBytes(GC.GetTotalMemory(false))}");
#endif
}

View File

@@ -1,179 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.IoC.Exceptions;
using Robust.Shared.Network.Messages;
using Robust.Shared.Utility;
namespace Robust.Server.Console
{
/// <inheritdoc />
internal class ConsoleShell : IConsoleShell
{
private const string SawmillName = "con";
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IServerNetManager _net = default!;
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IConGroupController _groupController = default!;
private readonly Dictionary<string, IClientCommand> _availableCommands =
new Dictionary<string, IClientCommand>();
/// <inheritdoc />
public IReadOnlyDictionary<string, IClientCommand> AvailableCommands => _availableCommands;
private void HandleRegistrationRequest(INetChannel senderConnection)
{
var netMgr = IoCManager.Resolve<IServerNetManager>();
var message = netMgr.CreateNetMessage<MsgConCmdReg>();
var counter = 0;
message.Commands = new MsgConCmdReg.Command[AvailableCommands.Count];
foreach (var command in AvailableCommands.Values)
{
message.Commands[counter++] = new MsgConCmdReg.Command
{
Name = command.Command,
Description = command.Description,
Help = command.Help
};
}
netMgr.ServerSendMessage(message, senderConnection);
}
/// <inheritdoc />
public void Initialize()
{
ReloadCommands();
// setup networking with clients
_net.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
_net.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
_net.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
message => HandleRegistrationRequest(message.MsgChannel));
}
/// <inheritdoc />
public void ReloadCommands()
{
// search for all client commands in all assemblies, and register them
_availableCommands.Clear();
foreach (var type in _reflectionManager.GetAllChildren<IClientCommand>())
{
var instance = (IClientCommand) Activator.CreateInstance(type, null)!;
if (AvailableCommands.TryGetValue(instance.Command, out var duplicate))
throw new InvalidImplementationException(instance.GetType(), typeof(IClientCommand),
$"Command name already registered: {instance.Command}, previous: {duplicate.GetType()}");
_availableCommands[instance.Command] = instance;
}
}
private void ProcessCommand(MsgConCmd message)
{
var text = message.Text;
var sender = message.MsgChannel;
var session = _players.GetSessionByChannel(sender);
_logMan.GetSawmill(SawmillName).Info($"{FormatPlayerString(session)}:{text}");
ExecuteCommand(session, text);
}
/// <inheritdoc />
public void ExecuteCommand(string command)
{
ExecuteCommand(null, command);
}
/// <inheritdoc />
public void ExecuteCommand(IPlayerSession? session, string command)
{
try
{
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
// missing cmdName
if (args.Count == 0)
return;
var cmdName = args[0];
if (_availableCommands.TryGetValue(cmdName, out var conCmd)) // command registered
{
if (session != null) // remote client
{
if (_groupController.CanCommand(session, cmdName)) // client has permission
{
args.RemoveAt(0);
conCmd.Execute(this, session, args.ToArray());
}
else
SendText(session, $"Unknown command: '{cmdName}'");
}
else // system console
{
args.RemoveAt(0);
conCmd.Execute(this, null, args.ToArray());
}
}
else
SendText(session, $"Unknown command: '{cmdName}'");
}
catch (Exception e)
{
_logMan.GetSawmill(SawmillName).Warning($"{FormatPlayerString(session)}: ExecuteError - {command}:\n{e}");
SendText(session, $"There was an error while executing the command: {e}");
}
}
/// <inheritdoc />
public void SendText(IPlayerSession? session, string? text)
{
if (session != null)
SendText(session.ConnectedClient, text);
else
_systemConsole.Print(text + "\n");
}
/// <inheritdoc />
public void SendText(INetChannel target, string? text)
{
var replyMsg = _net.CreateNetMessage<MsgConCmdAck>();
replyMsg.Text = text;
_net.ServerSendMessage(replyMsg, target);
}
private static string FormatPlayerString(IPlayerSession? session)
{
return session != null ? $"{session.Name}" : "[HOST]";
}
private class SudoCommand : IClientCommand
{
public string Command => "sudo";
public string Description => "sudo make me a sandwich";
public string Help => "sudo";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
var command = args[0];
var cArgs = args[1..].Select(CommandParsing.Escape);
shell.ExecuteCommand($"{command} {string.Join(' ', cArgs)}");
}
}
}
}

View File

@@ -0,0 +1,15 @@
using Robust.Shared.Console;
namespace Robust.Server.Console
{
/// <summary>
/// The server console shell that executes commands.
/// </summary>
public interface IServerConsoleHost : IConsoleHost
{
/// <summary>
/// Initializes the ConsoleShell service.
/// </summary>
void Initialize();
}
}

View File

@@ -1,4 +1,4 @@
namespace Robust.Server.Interfaces.Console
namespace Robust.Server.Console
{
/// <summary>
/// Wraps the system console.

View File

@@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
using Robust.Shared.Utility;
namespace Robust.Server.Console
{
/// <inheritdoc cref="IServerConsoleHost" />
internal class ServerConsoleHost : ConsoleHost, IServerConsoleHost
{
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly ISystemConsoleManager _systemConsole = default!;
[Dependency] private readonly IConGroupController _groupController = default!;
/// <inheritdoc />
public override void ExecuteCommand(ICommonSession? session, string command)
{
var shell = new ConsoleShell(this, session);
ExecuteInShell(shell, command);
}
private void ExecuteInShell(IConsoleShell shell, string command)
{
try
{
var args = new List<string>();
CommandParsing.ParseArguments(command, args);
// missing cmdName
if (args.Count == 0)
return;
var cmdName = args[0];
if (AvailableCommands.TryGetValue(cmdName, out var conCmd)) // command registered
{
if (shell.Player != null) // remote client
{
if (_groupController.CanCommand((IPlayerSession) shell.Player, cmdName)) // client has permission
{
args.RemoveAt(0);
conCmd.Execute(shell, command, args.ToArray());
}
else
shell.WriteLine($"Unknown command: '{cmdName}'");
}
else // system console
{
args.RemoveAt(0);
conCmd.Execute(shell, command, args.ToArray());
}
}
else
shell.WriteLine($"Unknown command: '{cmdName}'");
}
catch (Exception e)
{
LogManager.GetSawmill(SawmillName).Warning($"{FormatPlayerString(shell.Player)}: ExecuteError - {command}:\n{e}");
shell.WriteLine($"There was an error while executing the command: {e}");
}
}
public override void RemoteExecuteCommand(ICommonSession? session, string command)
{
//TODO: Server -> Client remote execute, just like how the client forwards the command
}
public override void WriteLine(ICommonSession? session, string text)
{
if (session is IPlayerSession playerSession)
SendText(playerSession, text);
else
SendText(null as IPlayerSession, text);
}
public override void WriteLine(ICommonSession? session, string text, Color color)
{
//TODO: Make colors work.
WriteLine(session, text);
}
/// <inheritdoc />
public void Initialize()
{
RegisterCommand("sudo", "sudo make me a sandwich", "sudo <command>", (shell, _, args) =>
{
var command = args[0];
var cArgs = args[1..].Select(CommandParsing.Escape);
var localShell = shell.ConsoleHost.LocalShell;
var sudoShell = new SudoShell(this, localShell, shell);
ExecuteInShell(sudoShell, $"{command} {string.Join(' ', cArgs)}");
});
LoadConsoleCommands();
// setup networking with clients
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
message => HandleRegistrationRequest(message.MsgChannel));
}
private void HandleRegistrationRequest(INetChannel senderConnection)
{
var netMgr = IoCManager.Resolve<IServerNetManager>();
var message = netMgr.CreateNetMessage<MsgConCmdReg>();
var counter = 0;
message.Commands = new MsgConCmdReg.Command[RegisteredCommands.Count];
foreach (var command in RegisteredCommands.Values)
{
message.Commands[counter++] = new MsgConCmdReg.Command
{
Name = command.Command,
Description = command.Description,
Help = command.Help
};
}
netMgr.ServerSendMessage(message, senderConnection);
}
private void ProcessCommand(MsgConCmd message)
{
var text = message.Text;
var sender = message.MsgChannel;
var session = _players.GetSessionByChannel(sender);
LogManager.GetSawmill(SawmillName).Info($"{FormatPlayerString(session)}:{text}");
ExecuteCommand(session, text);
}
/// <summary>
/// Sends a text string to the remote player.
/// </summary>
/// <param name="session">
/// Remote player to send the text message to. If this is null, the text is sent to the local
/// console.
/// </param>
/// <param name="text">Text message to send.</param>
private void SendText(IPlayerSession? session, string text)
{
if (session != null)
SendText(session.ConnectedClient, text);
else
_systemConsole.Print(text + "\n");
}
/// <summary>
/// Sends a text string to the remote console.
/// </summary>
/// <param name="target">Net channel to send the text string to.</param>
/// <param name="text">Text message to send.</param>
private void SendText(INetChannel target, string text)
{
var replyMsg = NetManager.CreateNetMessage<MsgConCmdAck>();
replyMsg.Text = text;
NetManager.ServerSendMessage(replyMsg, target);
}
private static string FormatPlayerString(IBaseSession? session)
{
return session != null ? $"{session.Name}" : "[HOST]";
}
private sealed class SudoShell : IConsoleShell
{
private readonly ServerConsoleHost _host;
private readonly IConsoleShell _owner;
private readonly IConsoleShell _sudoer;
public SudoShell(ServerConsoleHost host, IConsoleShell owner, IConsoleShell sudoer)
{
_host = host;
_owner = owner;
_sudoer = sudoer;
}
public IConsoleHost ConsoleHost => _host;
public bool IsServer => _owner.IsServer;
public ICommonSession? Player => _owner.Player;
public void ExecuteCommand(string command)
{
_host.ExecuteInShell(this, command);
}
public void RemoteExecuteCommand(string command)
{
_owner.RemoteExecuteCommand(command);
}
public void WriteLine(string text)
{
_owner.WriteLine(text);
_sudoer.WriteLine(text);
}
public void WriteLine(string text, Color color)
{
_owner.WriteLine(text, color);
_sudoer.WriteLine(text, color);
}
public void Clear()
{
_owner.Clear();
_sudoer.Clear();
}
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Shared.Asynchronous;
using Robust.Shared.IoC;
using Con = System.Console;
@@ -11,7 +10,7 @@ namespace Robust.Server.Console
{
internal sealed class SystemConsoleManager : ISystemConsoleManager, IPostInjectInit, IDisposable
{
[Dependency] private readonly IConsoleShell _conShell = default!;
[Dependency] private readonly IServerConsoleHost _conShell = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
[Dependency] private readonly IBaseServer _baseServer = default!;
@@ -66,7 +65,7 @@ namespace Robust.Server.Console
Con.WriteLine("> " + currentBuffer);
commandHistory.Add(commandHistory.Count, currentBuffer);
historyIndex = commandHistory.Count;
ExecuteCommand(currentBuffer);
_conShell.ExecuteCommand(currentBuffer);
currentBuffer = "";
internalCursor = 0;
break;
@@ -146,11 +145,6 @@ namespace Robust.Server.Console
}
}
private void ExecuteCommand(string commandLine)
{
_conShell.ExecuteCommand(commandLine);
}
public void Print(string text)
{
Con.Write(text);
@@ -181,7 +175,7 @@ namespace Robust.Server.Console
if (tabCompleteList.Count == 0)
{
tabCompleteList = _conShell.AvailableCommands.Keys.Where(key => key.StartsWith(currentBuffer)).ToList();
tabCompleteList = _conShell.RegisteredCommands.Keys.Where(key => key.StartsWith(currentBuffer)).ToList();
if (tabCompleteList.Count == 0)
{
return String.Empty;

View File

@@ -1,5 +1,3 @@
using Robust.Server.Interfaces.Console;
namespace Robust.Server.Console
{
internal sealed class SystemConsoleManagerDummy : ISystemConsoleManager

View File

@@ -1,13 +1,14 @@
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.IoC;
namespace Robust.Server.GameObjects.Components.Markers
namespace Robust.Server.GameObjects.Components
{
public class IgnorePauseComponent : Component
[RegisterComponent]
[ComponentReference(typeof(SharedIgnorePauseComponent))]
public sealed class IgnorePauseComponent : SharedIgnorePauseComponent
{
public override string Name => "IgnorePause";
public override void OnAdd()
{
base.OnAdd();

View File

@@ -162,7 +162,7 @@ namespace Robust.Server.GameObjects.Components.UserInterface
_stateDirty = true;
}
/// <summary>
/// Switches between closed and open for a specific client.
/// </summary>
@@ -183,8 +183,8 @@ namespace Robust.Server.GameObjects.Components.UserInterface
}
}
/// <summary>
/// Opens this interface for a specific client.
/// </summary>
@@ -263,6 +263,7 @@ namespace Robust.Server.GameObjects.Components.UserInterface
OnClosed?.Invoke(session);
_subscribedSessions.Remove(session);
_playerStateOverrides.Remove(session);
session.PlayerStatusChanged -= OnSessionOnPlayerStatusChanged;
if (_subscribedSessions.Count == 0)
{

View File

@@ -0,0 +1,18 @@
using Robust.Shared.GameObjects;
namespace Robust.Server.GameObjects
{
/// <summary>
/// Metadata component used to keep consistent UIDs inside map files cross saving.
/// </summary>
/// <remarks>
/// This component stores the previous map UID of entities from map load.
/// This can then be used to re-serialize the entity with the same UID for the merge driver to recognize.
/// </remarks>
public sealed class MapSaveIdComponent : Component
{
public override string Name => "MapSaveId";
public int Uid { get; set; }
}
}

View File

@@ -1,6 +1,5 @@
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.Eye;
using Robust.Server.GameObjects.Components.Markers;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
@@ -61,8 +60,6 @@ namespace Robust.Server.GameObjects
Register<ServerUserInterfaceComponent>();
RegisterReference<ServerUserInterfaceComponent, SharedUserInterfaceComponent>();
Register<IgnorePauseComponent>();
Register<TimerComponent>();
RegisterIgnore("AnimationPlayer");
@@ -73,6 +70,8 @@ namespace Robust.Server.GameObjects
Register<DebugExceptionInitializeComponent>();
Register<DebugExceptionStartupComponent>();
#endif
Register<MapSaveIdComponent>();
}
}
}

View File

@@ -419,7 +419,7 @@ namespace Robust.Server.GameObjects
var oldState =
(TransformComponent.TransformComponentState) state.ComponentStates[idx];
var newState = new TransformComponent.TransformComponentState(Vector2NaN,
oldState.Rotation, oldState.ParentID);
oldState.Rotation, oldState.ParentID, oldState.NoLocalRotation);
state.ComponentStates[idx] = newState;
seenMovers.Remove(uid);
ClearLastSeenTick(lSeen, uid);
@@ -457,7 +457,7 @@ namespace Robust.Server.GameObjects
new ComponentState[]
{
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation,
oldState.ParentID)
oldState.ParentID, oldState.NoLocalRotation)
}));
seenMovers.Remove(uid);
@@ -608,7 +608,7 @@ namespace Robust.Server.GameObjects
var oldState = (TransformComponent.TransformComponentState) state.ComponentStates[idx];
var newState =
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation, oldState.ParentID);
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation, oldState.ParentID, oldState.NoLocalRotation);
state.ComponentStates[idx] = newState;
@@ -661,7 +661,7 @@ namespace Robust.Server.GameObjects
var oldState = (TransformComponent.TransformComponentState) state.ComponentStates[idx];
var newState =
new TransformComponent.TransformComponentState(Vector2NaN, oldState.Rotation,
oldState.ParentID);
oldState.ParentID, oldState.NoLocalRotation);
state.ComponentStates[idx] = newState;
seenMovers.Remove(uid);

View File

@@ -1,19 +0,0 @@
using Robust.Server.Interfaces.Player;
using Robust.Shared.Console;
namespace Robust.Server.Interfaces.Console
{
/// <summary>
/// A command, executed from the debug console of a client.
/// </summary>
public interface IClientCommand : ICommand
{
/// <summary>
/// Executes the client command.
/// </summary>
/// <param name="shell">The console that executed this command.</param>
/// <param name="player">The player that ran this command. This is null if the command was ran by the server console.</param>
/// <param name="args">An array of all the parsed arguments.</param>
void Execute(IConsoleShell shell, IPlayerSession? player, string[] args);
}
}

View File

@@ -1,55 +0,0 @@
using System.Collections.Generic;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Network;
namespace Robust.Server.Interfaces.Console
{
/// <summary>
/// The server console shell that executes commands.
/// </summary>
public interface IConsoleShell
{
/// <summary>
/// A map of (commandName -> ICommand) of every registered command in the shell.
/// </summary>
IReadOnlyDictionary<string, IClientCommand> AvailableCommands { get; }
/// <summary>
/// Initializes the ConsoleShell service.
/// </summary>
void Initialize();
/// <summary>
/// Scans all loaded assemblies for console commands and registers them. This will NOT sync with connected clients, and
/// should only be used during server initialization.
/// </summary>
void ReloadCommands();
/// <summary>
/// Sends a text string to the remote player.
/// </summary>
/// <param name="session">Remote player to send the text message to. If this is null, the text is sent to the local console.</param>
/// <param name="text">Text message to send.</param>
void SendText(IPlayerSession? session, string? text);
/// <summary>
/// Sends a text string to the remote console.
/// </summary>
/// <param name="target">Net channel to send the text string to.</param>
/// <param name="text">Text message to send.</param>
void SendText(INetChannel target, string? text);
/// <summary>
/// Execute a command string on the local shell.
/// </summary>
/// <param name="command">Command string to execute.</param>
void ExecuteCommand(string command);
/// <summary>
/// Execute a command string as a player.
/// </summary>
/// <param name="player">Session of the remote player. If this is null, the command is executed as the local console.</param>
/// <param name="command">Command string to execute.</param>
void ExecuteCommand(IPlayerSession? player, string command);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Server.Maps;
using Robust.Shared.Map;
using YamlDotNet.RepresentationModel;
@@ -7,9 +8,11 @@ namespace Robust.Server.Interfaces.Maps
public interface IMapLoader
{
IMapGrid? LoadBlueprint(MapId mapId, string path);
IMapGrid? LoadBlueprint(MapId mapId, string path, MapLoadOptions options);
void SaveBlueprint(GridId gridId, string yamlPath);
void LoadMap(MapId mapId, string path);
void LoadMap(MapId mapId, string path, MapLoadOptions options);
void SaveMap(MapId mapId, string yamlPath);
event Action<YamlStream, string> LoadedMapData;

View File

@@ -1,6 +1,5 @@
using System;
using JetBrains.Annotations;
using Robust.Server.GameObjects.Components.Markers;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;

View File

@@ -0,0 +1,11 @@
namespace Robust.Server.Maps
{
public sealed class MapLoadOptions
{
/// <summary>
/// If true, UID components will be created for loaded entities
/// to maintain consistency upon subsequent savings.
/// </summary>
public bool StoreMapUids { get; set; }
}
}

View File

@@ -16,6 +16,7 @@ using Robust.Shared.GameObjects;
using System.Globalization;
using Robust.Shared.Interfaces.GameObjects;
using System.Linq;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.Interfaces.Serialization;
@@ -29,6 +30,8 @@ namespace Robust.Server.Maps
/// </summary>
public class MapLoader : IMapLoader
{
private static readonly MapLoadOptions DefaultLoadOptions = new();
private const int MapFormatVersion = 2;
[Dependency] private readonly IResourceManager _resMan = default!;
@@ -46,7 +49,8 @@ namespace Robust.Server.Maps
{
var grid = _mapManager.GetGrid(gridId);
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _componentManager, _prototypeManager);
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager,
_componentManager, _prototypeManager);
context.RegisterGrid(grid);
var root = context.Serialize();
var document = new YamlDocument(root);
@@ -68,6 +72,11 @@ namespace Robust.Server.Maps
/// <inheritdoc />
public IMapGrid? LoadBlueprint(MapId mapId, string path)
{
return LoadBlueprint(mapId, path, DefaultLoadOptions);
}
public IMapGrid? LoadBlueprint(MapId mapId, string path, MapLoadOptions options)
{
TextReader reader;
var resPath = new ResourcePath(path).ToRootedPath();
@@ -108,7 +117,8 @@ namespace Robust.Server.Maps
throw new InvalidDataException("Cannot instance map with multiple grids as blueprint.");
}
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _componentManager, _prototypeManager, (YamlMappingNode)data.RootNode, mapId);
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager,
_componentManager, _prototypeManager, (YamlMappingNode) data.RootNode, mapId, options);
context.Deserialize();
grid = context.Grids[0];
@@ -119,6 +129,14 @@ namespace Robust.Server.Maps
entity.RunMapInit();
}
}
if (_pauseManager.IsMapPaused(mapId))
{
foreach (var entity in context.Entities)
{
entity.Paused = true;
}
}
}
return grid;
@@ -128,7 +146,8 @@ namespace Robust.Server.Maps
public void SaveMap(MapId mapId, string yamlPath)
{
Logger.InfoS("map", $"Saving map {mapId} to {yamlPath}");
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _componentManager, _prototypeManager);
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager,
_componentManager, _prototypeManager);
foreach (var grid in _mapManager.GetAllMapGrids(mapId))
{
context.RegisterGrid(grid);
@@ -149,11 +168,16 @@ namespace Robust.Server.Maps
stream.Save(new YamlMappingFix(new Emitter(writer)), false);
}
}
Logger.InfoS("map", "Save completed!");
}
/// <inheritdoc />
public void LoadMap(MapId mapId, string path)
{
LoadMap(mapId, path, DefaultLoadOptions);
}
public void LoadMap(MapId mapId, string path, MapLoadOptions options)
{
TextReader reader;
var resPath = new ResourcePath(path).ToRootedPath();
@@ -188,7 +212,8 @@ namespace Robust.Server.Maps
LoadedMapData?.Invoke(data.Stream, resPath.ToString());
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager, _componentManager, _prototypeManager, (YamlMappingNode)data.RootNode, mapId);
var context = new MapContext(_mapManager, _tileDefinitionManager, _serverEntityManager, _pauseManager,
_componentManager, _prototypeManager, (YamlMappingNode) data.RootNode, mapId, options);
context.Deserialize();
if (!context.MapIsPostInit && _pauseManager.IsMapInitialized(mapId))
@@ -213,6 +238,7 @@ namespace Robust.Server.Maps
private readonly IComponentManager _componentManager;
private readonly IPrototypeManager _prototypeManager;
private readonly MapLoadOptions? _loadOptions;
private readonly Dictionary<GridId, int> GridIDMap = new();
public readonly List<IMapGrid> Grids = new();
@@ -225,8 +251,6 @@ namespace Robust.Server.Maps
private bool IsBlueprintMode => GridIDMap.Count == 1;
private int uidCounter;
private readonly YamlMappingNode RootNode;
private readonly MapId TargetMap;
@@ -239,7 +263,9 @@ namespace Robust.Server.Maps
public bool MapIsPostInit { get; private set; }
public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities, IPauseManager pauseManager, IComponentManager componentManager, IPrototypeManager prototypeManager)
public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs,
IServerEntityManagerInternal entities, IPauseManager pauseManager, IComponentManager componentManager,
IPrototypeManager prototypeManager)
{
_mapManager = maps;
_tileDefinitionManager = tileDefs;
@@ -251,14 +277,17 @@ namespace Robust.Server.Maps
RootNode = new YamlMappingNode();
}
public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs, IServerEntityManagerInternal entities,
IPauseManager pauseManager, IComponentManager componentManager, IPrototypeManager prototypeManager, YamlMappingNode node, MapId targetMapId)
public MapContext(IMapManagerInternal maps, ITileDefinitionManager tileDefs,
IServerEntityManagerInternal entities,
IPauseManager pauseManager, IComponentManager componentManager, IPrototypeManager prototypeManager,
YamlMappingNode node, MapId targetMapId, MapLoadOptions options)
{
_mapManager = maps;
_tileDefinitionManager = tileDefs;
_serverEntityManager = entities;
_pauseManager = pauseManager;
_componentManager = componentManager;
_loadOptions = options;
RootNode = node;
TargetMap = targetMapId;
@@ -438,8 +467,8 @@ namespace Robust.Server.Maps
var newId = new GridId?();
YamlGridSerializer.DeserializeGrid(
_mapManager, TargetMap, ref newId,
(YamlMappingNode)grid["settings"],
(YamlSequenceNode)grid["chunks"],
(YamlMappingNode) grid["settings"],
(YamlSequenceNode) grid["chunks"],
_tileMap!,
_tileDefinitionManager
);
@@ -489,6 +518,12 @@ namespace Robust.Server.Maps
Entities.Add(entity);
UidEntityMap.Add(uid, entity.Uid);
_entitiesToDeserialize.Add((entity, entityDef));
if (_loadOptions!.StoreMapUids)
{
var comp = entity.AddComponent<MapSaveIdComponent>();
comp.Uid = uid;
}
}
}
@@ -501,7 +536,7 @@ namespace Robust.Server.Maps
{
foreach (var compData in componentList)
{
CurrentReadingEntityComponents[compData["type"].AsString()] = (YamlMappingNode)compData;
CurrentReadingEntityComponents[compData["type"].AsString()] = (YamlMappingNode) compData;
}
}
@@ -605,15 +640,55 @@ namespace Robust.Server.Maps
private void PopulateEntityList()
{
var withUid = new List<MapSaveIdComponent>();
var withoutUid = new List<IEntity>();
var takenIds = new HashSet<int>();
foreach (var entity in _serverEntityManager.GetEntities())
{
if (IsMapSavable(entity))
{
var uid = uidCounter++;
EntityUidMap.Add(entity.Uid, uid);
Entities.Add(entity);
if (entity.TryGetComponent(out MapSaveIdComponent? mapSaveId))
{
withUid.Add(mapSaveId);
}
else
{
withoutUid.Add(entity);
}
}
}
// Go over entities with a MapSaveIdComponent and assign those.
foreach (var mapIdComp in withUid)
{
var uid = mapIdComp.Uid;
if (takenIds.Contains(uid))
{
// Duplicate ID. Just pretend it doesn't have an ID and use the without path.
withoutUid.Add(mapIdComp.Owner);
}
else
{
EntityUidMap.Add(mapIdComp.Owner.Uid, uid);
takenIds.Add(uid);
}
}
var uidCounter = 0;
foreach (var entity in withoutUid)
{
while (takenIds.Contains(uidCounter))
{
// Find next available UID.
uidCounter += 1;
}
EntityUidMap.Add(entity.Uid, uidCounter);
takenIds.Add(uidCounter);
}
}
private void WriteEntitySection()
@@ -621,7 +696,7 @@ namespace Robust.Server.Maps
var entities = new YamlSequenceNode();
RootNode.Add("entities", entities);
foreach (var entity in Entities)
foreach (var entity in Entities.OrderBy(e => EntityUidMap[e.Uid]))
{
CurrentWritingEntity = entity;
var mapping = new YamlMappingNode
@@ -638,6 +713,9 @@ namespace Robust.Server.Maps
// See engine#636 for why the Distinct() call.
foreach (var component in entity.GetAllComponents())
{
if (component is MapSaveIdComponent)
continue;
var compMapping = new YamlMappingNode();
CurrentWritingComponent = component.Name;
var compSerializer = YamlObjectSerializer.NewWriter(compMapping, this);
@@ -683,6 +761,7 @@ namespace Robust.Server.Maps
return true;
}
}
if (type == typeof(EntityUid))
{
if (node.AsString() == "null")
@@ -694,7 +773,8 @@ namespace Robust.Server.Maps
var val = node.AsInt();
if (val >= Entities.Count)
{
Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.", val);
Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.",
val);
}
else
{
@@ -702,12 +782,14 @@ namespace Robust.Server.Maps
return true;
}
}
if (typeof(IEntity).IsAssignableFrom(type))
{
var val = node.AsInt();
if (val >= Entities.Count)
{
Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.", val);
Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.",
val);
}
else
{
@@ -715,6 +797,7 @@ namespace Robust.Server.Maps
return true;
}
}
obj = null;
return false;
}
@@ -766,6 +849,7 @@ namespace Robust.Server.Maps
return true;
}
}
node = null;
return false;
}
@@ -878,7 +962,7 @@ namespace Robust.Server.Maps
}
Stream = stream;
GridCount = ((YamlSequenceNode)RootNode["grids"]).Children.Count;
GridCount = ((YamlSequenceNode) RootNode["grids"]).Children.Count;
}
}
}

View File

@@ -1,10 +1,9 @@
using Robust.Server.Console;
using Robust.Server.Console;
using Robust.Server.DataMetrics;
using Robust.Server.Debugging;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Debugging;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.GameState;
@@ -50,7 +49,7 @@ namespace Robust.Server
IoCManager.Register<IBaseServerInternal, BaseServer>();
IoCManager.Register<IComponentFactory, ServerComponentFactory>();
IoCManager.Register<IConGroupController, ConGroupController>();
IoCManager.Register<IConsoleShell, ConsoleShell>();
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
IoCManager.Register<IEntityManager, ServerEntityManager>();
IoCManager.Register<IEntityNetworkManager, ServerEntityNetworkManager>();
IoCManager.Register<IServerEntityNetworkManager, ServerEntityNetworkManager>();

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Robust.Server.GameObjects.Components.Markers;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
@@ -26,7 +26,6 @@ namespace Robust.Server.Timing
_pausedMaps.Add(mapId);
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
{
if (entity.HasComponent<IgnorePauseComponent>()) continue;
entity.Paused = true;
}
}
@@ -60,7 +59,7 @@ namespace Robust.Server.Timing
public void DoGridMapInitialize(GridId gridId)
{
var mapId = _mapManager.GetGrid(gridId).ParentMapId;
foreach (var entity in _entityManager.GetEntitiesInMap(mapId))
{
if (entity.Transform.GridID != gridId)

Some files were not shown because too many files have changed in this diff Show More