* ogh

* i should save my work

* ogh

* hhcdfhjbghshbxdfhghshc
- lots of bugs in parsing still
- invocation is a stub

* expr parsing works

* awawa

* Saving work

* Improve APIs a bit all around, add shortcuts.

* awa

* awa

* AAAAAA

* save work

* Move shit to engine

* lord

* bql is kill

* forgot the fucking bike rack

* bql is kill for real

* pjb will kill me

* aughfhbdj

* adgddf

* gdsgvfvxshngfgh

* b

* hfsjhghj

* a

* tf you mean i have to document it

* follow C# standards

* Assorted cleanup and documentation pass, minor bugfix in ValueRefParser.

* Start porting old commands, remove that pesky prefix in favor of integrating with the shell.

* bw

* Fix valueref up a bit, improve autocomplete for it.

* awa

* fix tests

* git shut up

* Arithmetic commands.

* parse improvements

* Update engine.

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
This commit is contained in:
Moony
2023-08-02 16:09:08 -05:00
committed by GitHub
parent ad61c21c01
commit e2b22a4cd8
48 changed files with 1204 additions and 793 deletions

View File

@@ -12,7 +12,6 @@ namespace Content.Server.Administration
/// </remarks>
/// <seealso cref="AnyCommandAttribute"/>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
[BaseTypeRequired(typeof(IConsoleCommand))]
[MeansImplicitUse]
public sealed class AdminCommandAttribute : Attribute
{

View File

@@ -1,47 +0,0 @@
using System.Linq;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Spawn)]
sealed class DeleteEntitiesWithComponent : IConsoleCommand
{
public string Command => "deleteewc";
public string Description => Loc.GetString("delete-entities-with-component-command-description");
public string Help => Loc.GetString("delete-entities-with-component-command-help-text");
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1)
{
shell.WriteLine(Help);
return;
}
var factory = IoCManager.Resolve<IComponentFactory>();
var components = new List<Type>();
foreach (var arg in args)
{
components.Add(factory.GetRegistration(arg).Type);
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var entitiesWithComponents = components.Select(c => entityManager.GetAllComponents(c).Select(x => x.Uid));
var entitiesWithAllComponents = entitiesWithComponents.Skip(1).Aggregate(new HashSet<EntityUid>(entitiesWithComponents.First()), (h, e) => { h.IntersectWith(e); return h; });
var count = 0;
foreach (var entity in entitiesWithAllComponents)
{
entityManager.DeleteEntity(entity);
count += 1;
}
shell.WriteLine(Loc.GetString("delete-entities-with-component-command-deleted-components",("count", count)));
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Linq;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Spawn)]
public sealed class DeleteEntitiesWithId : IConsoleCommand
{
public string Command => "deleteewi";
public string Description => "Deletes entities with the specified prototype ID.";
public string Help => $"Usage: {Command} <prototypeID>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine(Help);
return;
}
var id = args[0].ToLower();
var entityManager = IoCManager.Resolve<IEntityManager>();
var entities = entityManager.GetEntities().Where(e => entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID.ToLower() == id);
var i = 0;
foreach (var entity in entities)
{
entityManager.DeleteEntity(entity);
i++;
}
shell.WriteLine($"Deleted all entities with id {id}. Occurrences: {i}");
}
}
}

View File

@@ -1,39 +0,0 @@
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Spawn)]
public sealed class DeleteEntityCommand : IConsoleCommand
{
public string Command => "deleteentity";
public string Description => "Deletes an entity with the given id.";
public string Help => $"Usage: {Command} <id>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine($"Invalid amount of arguments.\n{Help}");
return;
}
if (!EntityUid.TryParse(args[0], out var id))
{
shell.WriteLine($"{args[0]} is not a valid entity id.");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.EntityExists(id))
{
shell.WriteLine($"No entity found with id {id}.");
return;
}
entityManager.DeleteEntity(id);
shell.WriteLine($"Deleted entity with id {id}.");
}
}
}

View File

@@ -1,68 +0,0 @@
using System.Linq;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Mapping)]
public sealed class FindEntitiesWithComponents : IConsoleCommand
{
public string Command => "findentitieswithcomponents";
public string Description => "Finds entities with all of the specified components.";
public string Help => $"{Command} <componentName1> <componentName2>...";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length == 0)
{
shell.WriteLine($"Invalid amount of arguments: {args.Length}.\n{Help}");
return;
}
var components = new List<Type>();
var componentFactory = IoCManager.Resolve<IComponentFactory>();
var invalidArgs = new List<string>();
foreach (var arg in args)
{
if (!componentFactory.TryGetRegistration(arg, out var registration))
{
invalidArgs.Add(arg);
continue;
}
components.Add(registration.Type);
}
if (invalidArgs.Count > 0)
{
shell.WriteLine($"No component found for component names: {string.Join(", ", invalidArgs)}");
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var entityIds = new HashSet<string>();
var entitiesWithComponents = components.Select(c => entityManager.GetAllComponents(c).Select(x => x.Uid)).ToArray();
var entitiesWithAllComponents = entitiesWithComponents.Skip(1).Aggregate(new HashSet<EntityUid>(entitiesWithComponents.First()), (h, e) => { h.IntersectWith(e); return h; });
foreach (var entity in entitiesWithAllComponents)
{
if (entityManager.GetComponent<MetaDataComponent>(entity).EntityPrototype is not { } prototypeId)
{
continue;
}
entityIds.Add(prototypeId.ID);
}
if (entityIds.Count == 0)
{
shell.WriteLine($"No entities found with components {string.Join(", ", args)}.");
return;
}
shell.WriteLine($"{entityIds.Count} entities found:\n{string.Join("\n", entityIds)}");
}
}
}

View File

@@ -1,48 +0,0 @@
using Content.Shared.Administration;
using Content.Shared.Rejuvenate;
using Robust.Server.Player;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Admin)]
public sealed class RejuvenateCommand : IConsoleCommand
{
public string Command => "rejuvenate";
public string Description => Loc.GetString("rejuvenate-command-description");
public string Help => Loc.GetString("rejuvenate-command-help-text");
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 && shell.Player is IPlayerSession player) //Try to heal the users mob if applicable
{
shell.WriteLine(Loc.GetString("rejuvenate-command-self-heal-message"));
if (player.AttachedEntity == null)
{
shell.WriteLine(Loc.GetString("rejuvenate-command-no-entity-attached-message"));
return;
}
PerformRejuvenate(player.AttachedEntity.Value);
}
var entityManager = IoCManager.Resolve<IEntityManager>();
foreach (var arg in args)
{
if (!EntityUid.TryParse(arg, out var entity) || !entityManager.EntityExists(entity))
{
shell.WriteLine(Loc.GetString("shell-could-not-find-entity",("entity", arg)));
continue;
}
PerformRejuvenate(entity);
}
}
public static void PerformRejuvenate(EntityUid target)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent());
}
}
}

View File

@@ -1,84 +0,0 @@
using Content.Server.Commands;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Content.Shared.Roles;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
namespace Content.Server.Administration.Commands.Station;
[AdminCommand(AdminFlags.Round)]
public sealed class AdjustStationJobCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entSysManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string Command => "adjstationjob";
public string Description => "Adjust the job manifest on a station.";
public string Help => "adjstationjob <station id> <job id> <amount>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
var stationJobs = _entSysManager.GetEntitySystem<StationJobsSystem>();
if (!EntityUid.TryParse(args[0], out var station) || !_entityManager.HasComponent<StationDataComponent>(station))
{
shell.WriteError(Loc.GetString("shell-argument-station-id-invalid", ("index", 1)));
return;
}
if (!_prototypeManager.TryIndex<JobPrototype>(args[1], out var job))
{
shell.WriteError(Loc.GetString("shell-argument-must-be-prototype",
("index", 2), ("prototypeName", nameof(JobPrototype))));
return;
}
if (!int.TryParse(args[2], out var amount) || amount < -1)
{
shell.WriteError(Loc.GetString("shell-argument-number-must-be-between",
("index", 3), ("lower", -1), ("upper", int.MaxValue)));
return;
}
if (amount == -1)
{
stationJobs.MakeJobUnlimited(station, job);
return;
}
stationJobs.TrySetJobSlot(station, job, amount, true);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
var options = ContentCompletionHelper.StationIds(_entityManager);
return CompletionResult.FromHintOptions(options, "<station id>");
}
if (args.Length == 2)
{
var options = CompletionHelper.PrototypeIDs<JobPrototype>();
return CompletionResult.FromHintOptions(options, "<job id>");
}
if (args.Length == 3)
{
return CompletionResult.FromHint("<amount>");
}
return CompletionResult.Empty;
}
}

View File

@@ -1,55 +0,0 @@
using Content.Server.Commands;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands.Station;
[AdminCommand(AdminFlags.Admin)]
public sealed class ListStationJobsCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entSysManager = default!;
public string Command => "lsstationjobs";
public string Description => "Lists all jobs on the given station.";
public string Help => "lsstationjobs <station id>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
var stationSystem = _entSysManager.GetEntitySystem<StationSystem>();
var stationJobs = _entSysManager.GetEntitySystem<StationJobsSystem>();
if (!EntityUid.TryParse(args[0], out var station) || !_entityManager.HasComponent<StationJobsComponent>(station))
{
shell.WriteError(Loc.GetString("shell-argument-station-id-invalid", ("index", 1)));
return;
}
foreach (var (job, amount) in stationJobs.GetJobs(station))
{
var amountText = amount is null ? "Infinite" : amount.ToString();
shell.WriteLine($"{job}: {amountText}");
}
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
var options = ContentCompletionHelper.StationIds(_entityManager);
return CompletionResult.FromHintOptions(options, "<station id>");
}
return CompletionResult.Empty;
}
}

View File

@@ -1,29 +0,0 @@
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands.Station;
[AdminCommand(AdminFlags.Admin)]
public sealed class ListStationsCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public string Command => "lsstations";
public string Description => "List all active stations";
public string Help => "lsstations";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
var query = _entityManager.EntityQueryEnumerator<StationDataComponent>();
while (query.MoveNext(out var station, out _))
{
var name = _entityManager.GetComponent<MetaDataComponent>(station).EntityName;
shell.WriteLine($"{station, -10} | {name}");
}
}
}

View File

@@ -1,55 +0,0 @@
using Content.Server.Commands;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands.Station;
[AdminCommand(AdminFlags.Admin)]
public sealed class RenameStationCommand : IConsoleCommand
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entSysManager = default!;
public string Command => "renamestation";
public string Description => "Renames the given station";
public string Help => "renamestation <station id> <name>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
var stationSystem = _entSysManager.GetEntitySystem<StationSystem>();
if (!EntityUid.TryParse(args[0], out var station) || !_entityManager.HasComponent<StationDataComponent>(station))
{
shell.WriteError(Loc.GetString("shell-argument-station-id-invalid", ("index", 1)));
return;
}
stationSystem.RenameStation(station, args[1]);
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
var options = ContentCompletionHelper.StationIds(_entityManager);
return CompletionResult.FromHintOptions(options, "<station id>");
}
if (args.Length == 2)
{
return CompletionResult.FromHint("<name>");
}
return CompletionResult.Empty;
}
}

View File

@@ -1,113 +0,0 @@
using Content.Shared.Administration;
using Content.Shared.Tag;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Debug)]
public sealed class AddTagCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public override string Command => "addtag";
public override string Description => Loc.GetString("addtag-command-description");
public override string Help => Loc.GetString("addtag-command-help");
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!EntityUid.TryParse(args[0], out var entityUid))
{
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
if (!_entityManager.TrySystem(out TagSystem? tagSystem))
return;
_entityManager.EnsureComponent<TagComponent>(entityUid);
if (tagSystem.TryAddTag(entityUid, args[1]))
{
shell.WriteLine(Loc.GetString("addtag-command-success", ("tag", args[1]), ("target", entityUid)));
}
else
{
shell.WriteError(Loc.GetString("addtag-command-fail", ("tag", args[1]), ("target", entityUid)));
}
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
return CompletionResult.FromHint(Loc.GetString("shell-argument-uid"));
}
if (args.Length == 2)
{
return CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<TagPrototype>(),
Loc.GetString("tag-command-arg-tag"));
}
return CompletionResult.Empty;
}
}
[AdminCommand(AdminFlags.Debug)]
public sealed class RemoveTagCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public override string Command => "removetag";
public override string Description => Loc.GetString("removetag-command-description");
public override string Help => Loc.GetString("removetag-command-help");
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!EntityUid.TryParse(args[0], out var entityUid))
{
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
if (!_entityManager.TrySystem(out TagSystem? tagSystem))
return;
if (tagSystem.RemoveTag(entityUid, args[1]))
{
shell.WriteLine(Loc.GetString("removetag-command-success", ("tag", args[1]), ("target", entityUid)));
}
else
{
shell.WriteError(Loc.GetString("removetag-command-fail", ("tag", args[1]), ("target", entityUid)));
}
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
return CompletionResult.FromHint(Loc.GetString("shell-argument-uid"));
}
if (args.Length == 2&& EntityUid.TryParse(args[0], out var entityUid) && _entityManager.TryGetComponent(entityUid, out TagComponent? tagComponent))
{
return CompletionResult.FromHintOptions(tagComponent.Tags,
Loc.GetString("tag-command-arg-tag"));
}
return CompletionResult.Empty;
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -12,7 +13,11 @@ using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Utility;
@@ -28,6 +33,7 @@ namespace Content.Server.Administration.Managers
[Dependency] private readonly IResourceManager _res = default!;
[Dependency] private readonly IServerConsoleHost _consoleHost = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly ToolshedManager _toolshed = default!;
private readonly Dictionary<IPlayerSession, AdminReg> _admins = new();
private readonly HashSet<NetUserId> _promotedPlayers = new();
@@ -41,6 +47,7 @@ namespace Content.Server.Administration.Managers
public IEnumerable<IPlayerSession> AllAdmins => _admins.Select(p => p.Key);
private readonly AdminCommandPermissions _commandPermissions = new();
private readonly AdminCommandPermissions _toolshedCommandPermissions = new();
public bool IsAdmin(IPlayerSession session, bool includeDeAdmin = false)
{
@@ -196,11 +203,37 @@ namespace Content.Server.Administration.Managers
}
}
foreach (var spec in _toolshed.AllCommands())
{
var (isAvail, flagsReq) = GetRequiredFlag(spec.Cmd);
if (!isAvail)
{
continue;
}
if (flagsReq.Length != 0)
{
_toolshedCommandPermissions.AdminCommands.TryAdd(spec.Cmd.Name, flagsReq);
}
else
{
_toolshedCommandPermissions.AnyCommands.Add(spec.Cmd.Name);
}
}
// Load flags for engine commands, since those don't have the attributes.
if (_res.TryContentFileRead(new ResPath("/engineCommandPerms.yml"), out var efs))
{
_commandPermissions.LoadPermissionsFromStream(efs);
}
if (_res.TryContentFileRead(new ResPath("/toolshedEngineCommandPerms.yml"), out var toolshedPerms))
{
_toolshedCommandPermissions.LoadPermissionsFromStream(toolshedPerms);
}
_toolshed.ActivePermissionController = this;
}
public void PromoteHost(IPlayerSession player)
@@ -366,6 +399,26 @@ namespace Content.Server.Administration.Managers
return Equals(addr, System.Net.IPAddress.Loopback) || Equals(addr, System.Net.IPAddress.IPv6Loopback);
}
public bool TryGetCommandFlags(CommandSpec command, out AdminFlags[]? flags)
{
var cmdName = command.Cmd.Name;
if (_toolshedCommandPermissions.AnyCommands.Contains(cmdName))
{
// Anybody can use this command.
flags = null;
return true;
}
if (_toolshedCommandPermissions.AdminCommands.TryGetValue(cmdName, out flags))
{
return true;
}
flags = null;
return false;
}
public bool CanCommand(IPlayerSession session, string cmdName)
{
if (_commandPermissions.AnyCommands.Contains(cmdName))
@@ -398,7 +451,51 @@ namespace Content.Server.Administration.Managers
return false;
}
private static (bool isAvail, AdminFlags[] flagsReq) GetRequiredFlag(IConsoleCommand cmd)
public bool CheckInvokable(CommandSpec command, ICommonSession? user, out IConError? error)
{
if (user is null)
{
error = null;
return true; // Server console.
}
var name = command.Cmd.Name;
if (!TryGetCommandFlags(command, out var flags))
{
// Command is missing permissions.
error = new CommandPermissionsUnassignedError(command);
return false;
}
if (flags is null)
{
// Anyone can execute this.
error = null;
return true;
}
var data = GetAdminData((IPlayerSession)user);
if (data == null)
{
// Player isn't an admin.
error = new NoPermissionError(command);
return false;
}
foreach (var flag in flags)
{
if (data.HasFlag(flag))
{
error = null;
return true;
}
}
error = new NoPermissionError(command);
return false;
}
private static (bool isAvail, AdminFlags[] flagsReq) GetRequiredFlag(object cmd)
{
MemberInfo type = cmd.GetType();
@@ -472,3 +569,28 @@ namespace Content.Server.Administration.Managers
}
}
}
public record struct CommandPermissionsUnassignedError(CommandSpec Command) : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromMarkup($"The command {Command.FullName()} is missing permission flags and cannot be executed.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}
public record struct NoPermissionError(CommandSpec Command) : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromMarkup($"You do not have permission to execute {Command.FullName()}");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Administration;
using Content.Shared.Administration.Managers;
using Robust.Server.Player;
using Robust.Shared.Toolshed;
namespace Content.Server.Administration.Managers
@@ -87,5 +88,7 @@ namespace Content.Server.Administration.Managers
void Initialize();
void PromoteHost(IPlayerSession player);
bool TryGetCommandFlags(CommandSpec command, out AdminFlags[]? flags);
}
}

View File

@@ -117,7 +117,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
Act = () =>
{
RejuvenateCommand.PerformRejuvenate(args.Target);
_rejuvenate.PerformRejuvenate(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-rejuvenate-description"),

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Server.Administration.Commands;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
@@ -31,6 +32,7 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Toolshed;
using Robust.Shared.Utility;
using static Content.Shared.Configurable.ConfigurationComponent;
@@ -55,6 +57,8 @@ namespace Content.Server.Administration.Systems
[Dependency] private readonly PrayerSystem _prayerSystem = default!;
[Dependency] private readonly EuiManager _eui = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly ToolshedManager _toolshed = default!;
[Dependency] private readonly RejuvenateSystem _rejuvenate = default!;
private readonly Dictionary<IPlayerSession, EditSolutionsEui> _openSolutionUis = new();
@@ -78,6 +82,14 @@ namespace Content.Server.Administration.Systems
if (_adminManager.IsAdmin(player))
{
Verb mark = new();
mark.Text = Loc.GetString("toolshed-verb-mark");
mark.Message = Loc.GetString("toolshed-verb-mark-description");
mark.Category = VerbCategory.Admin;
mark.Act = () => _toolshed.InvokeCommand(player, "=> $marked", Enumerable.Repeat(args.Target, 1), out _);
mark.Impact = LogImpact.Low;
args.Verbs.Add(mark);
if (TryComp(args.Target, out ActorComponent? targetActor))
{
// AdminHelp
@@ -188,8 +200,6 @@ namespace Content.Server.Administration.Systems
Category = VerbCategory.Admin,
Act = () =>
{
if (!TryComp<ActorComponent>(args.Target, out var actor)) return;
_console.ExecuteCommand(player, $"respawn {actor.PlayerSession.Name}");
},
ConfirmationPopup = true,
@@ -229,7 +239,7 @@ namespace Content.Server.Administration.Systems
Text = Loc.GetString("rejuvenate-verb-get-data-text"),
Category = VerbCategory.Debug,
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png")),
Act = () => RejuvenateCommand.PerformRejuvenate(args.Target),
Act = () => _rejuvenate.PerformRejuvenate(args.Target),
Impact = LogImpact.Medium
};
args.Verbs.Add(verb);
@@ -247,11 +257,11 @@ namespace Content.Server.Administration.Systems
Act = () =>
{
MakeSentientCommand.MakeSentient(args.Target, EntityManager);
var mind = player.ContentData()?.Mind;
if (mind == null)
return;
_mindSystem.TransferTo(mind, args.Target, ghostCheckOverride: true);
},
Impact = LogImpact.High,

View File

@@ -0,0 +1,11 @@
using Content.Shared.Rejuvenate;
namespace Content.Server.Administration.Systems;
public sealed class RejuvenateSystem : EntitySystem
{
public void PerformRejuvenate(EntityUid target)
{
RaiseLocalEvent(target, new RejuvenateEvent());
}
}

View File

@@ -0,0 +1,24 @@
using Content.Server.Administration.Managers;
using Content.Shared.Administration;
using Robust.Server.Player;
using Robust.Shared.Toolshed;
namespace Content.Server.Administration.Toolshed;
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class AdminsCommand : ToolshedCommand
{
[Dependency] private readonly IAdminManager _admin = default!;
[CommandImplementation("active")]
public IEnumerable<IPlayerSession> Active()
{
return _admin.ActiveAdmins;
}
[CommandImplementation("all")]
public IEnumerable<IPlayerSession> All()
{
return _admin.AllAdmins;
}
}

View File

@@ -0,0 +1,16 @@
using Content.Shared.Administration;
using Robust.Shared.Toolshed;
namespace Content.Server.Administration.Toolshed;
[ToolshedCommand, AnyCommand]
public sealed class MarkedCommand : ToolshedCommand
{
[CommandImplementation]
public IEnumerable<EntityUid> Marked([CommandInvocationContext] IInvocationContext ctx)
{
var res = (IEnumerable<EntityUid>?)ctx.ReadVar("marked");
res ??= Array.Empty<EntityUid>();
return res;
}
}

View File

@@ -0,0 +1,22 @@
using Content.Server.Administration.Systems;
using Content.Shared.Administration;
using Robust.Shared.Toolshed;
namespace Content.Server.Administration.Toolshed;
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class RejuvenateCommand : ToolshedCommand
{
private RejuvenateSystem? _rejuvenate;
[CommandImplementation]
public IEnumerable<EntityUid> Rejuvenate([PipedArgument] IEnumerable<EntityUid> input)
{
_rejuvenate ??= GetSys<RejuvenateSystem>();
foreach (var i in input)
{
_rejuvenate.PerformRejuvenate(i);
yield return i;
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Linq;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Administration;
using Content.Shared.Chemistry.Components;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
namespace Content.Server.Administration.Toolshed;
[ToolshedCommand, AdminCommand(AdminFlags.Debug)]
public sealed class SolutionCommand : ToolshedCommand
{
private SolutionContainerSystem? _solutionContainer;
[CommandImplementation("get")]
public SolutionRef? Get(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string> name
)
{
_solutionContainer ??= GetSys<SolutionContainerSystem>();
_solutionContainer.TryGetSolution(input, name.Evaluate(ctx)!, out var solution);
if (solution is not null)
return new SolutionRef(input, solution);
return null;
}
[CommandImplementation("get")]
public IEnumerable<SolutionRef> Get(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<string> name
)
{
return input.Select(x => Get(ctx, x, name)).Where(x => x is not null).Cast<SolutionRef>();
}
}
public readonly record struct SolutionRef(EntityUid Owner, Solution Solution)
{
public override string ToString()
{
return $"{Owner} {Solution}";
}
}

View File

@@ -0,0 +1,106 @@
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Tag;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Server.Administration.Toolshed;
[ToolshedCommand, AdminCommand(AdminFlags.Debug)]
public sealed class TagCommand : ToolshedCommand
{
private TagSystem? _tag;
[CommandImplementation("list")]
public IEnumerable<string> List([PipedArgument] IEnumerable<EntityUid> ent)
{
return ent.SelectMany(x =>
{
if (TryComp<TagComponent>(x, out var tags))
// Note: Cast is required for C# to figure out the type signature.
return (IEnumerable<string>)tags.Tags;
return Array.Empty<string>();
});
}
[CommandImplementation("add")]
public EntityUid Add(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> @ref
)
{
_tag ??= GetSys<TagSystem>();
_tag.AddTag(input, @ref.Evaluate(ctx)!);
return input;
}
[CommandImplementation("add")]
public IEnumerable<EntityUid> Add(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> @ref
)
=> input.Select(x => Add(ctx, x, @ref));
[CommandImplementation("rm")]
public EntityUid Rm(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> @ref
)
{
_tag ??= GetSys<TagSystem>();
_tag.RemoveTag(input, @ref.Evaluate(ctx)!);
return input;
}
[CommandImplementation("rm")]
public IEnumerable<EntityUid> Rm(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> @ref
)
=> input.Select(x => Rm(ctx, x, @ref));
[CommandImplementation("addmany")]
public EntityUid AddMany(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<IEnumerable<string>, IEnumerable<string>> @ref
)
{
_tag ??= GetSys<TagSystem>();
_tag.AddTags(input, @ref.Evaluate(ctx)!);
return input;
}
[CommandImplementation("addmany")]
public IEnumerable<EntityUid> AddMany(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<IEnumerable<string>, IEnumerable<string>> @ref
)
=> input.Select(x => AddMany(ctx, x, @ref));
[CommandImplementation("rmmany")]
public EntityUid RmMany(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<IEnumerable<string>, IEnumerable<string>> @ref
)
{
_tag ??= GetSys<TagSystem>();
_tag.RemoveTags(input, @ref.Evaluate(ctx)!);
return input;
}
[CommandImplementation("rmmany")]
public IEnumerable<EntityUid> RmMany(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<IEnumerable<string>, IEnumerable<string>> @ref
)
=> input.Select(x => RmMany(ctx, x, @ref));
}

View File

@@ -1,56 +0,0 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Bql;
using Content.Shared.Eui;
using Robust.Server.Bql;
using Robust.Server.Player;
using Robust.Shared.Console;
namespace Content.Server.Bql;
[AdminCommand(AdminFlags.Query)]
public sealed class BqlSelectCommand : LocalizedCommands
{
[Dependency] private readonly IBqlQueryManager _bql = default!;
[Dependency] private readonly EuiManager _euiManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public override string Command => "bql_select";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (shell.Player == null)
{
shell.WriteError(LocalizationManager.GetString("cmd-bql_select-err-server-shell"));
return;
}
var (entities, rest) = _bql.SimpleParseAndExecute(argStr["bql_select".Length..]);
if (!string.IsNullOrWhiteSpace(rest))
shell.WriteLine(LocalizationManager.GetString("cmd-bql_select-err-rest", ("rest", rest)));
var ui = new BqlResultsEui(
entities.Select(e => (_entityManager.GetComponent<MetaDataComponent>(e).EntityName, e)).ToArray()
);
_euiManager.OpenEui(ui, (IPlayerSession) shell.Player);
_euiManager.QueueStateUpdate(ui);
}
}
internal sealed class BqlResultsEui : BaseEui
{
private readonly (string name, EntityUid entity)[] _entities;
public BqlResultsEui((string name, EntityUid entity)[] entities)
{
_entities = entities;
}
public override EuiStateBase GetNewState()
{
return new BqlResultsEuiState(_entities);
}
}

View File

@@ -1,137 +0,0 @@
using System.Linq;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Power.Components;
using Content.Shared.Tag;
using Robust.Server.Bql;
namespace Content.Server.Bql
{
public sealed class QuerySelectors
{
[RegisterBqlQuerySelector]
public sealed class MindfulQuerySelector : BqlQuerySelector
{
public override string Token => "mindful";
public override QuerySelectorArgument[] Arguments => Array.Empty<QuerySelectorArgument>();
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input,
IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return input.Where(e =>
{
if (entityManager.TryGetComponent<MindContainerComponent>(e, out var mind))
return (mind.Mind?.VisitingEntity == e) ^ isInverted;
return isInverted;
});
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(
entityManager.EntityQuery<MindContainerComponent>().Select(x => x.Owner),
arguments, isInverted, entityManager);
}
}
[RegisterBqlQuerySelector]
public sealed class TaggedQuerySelector : BqlQuerySelector
{
public override string Token => "tagged";
public override QuerySelectorArgument[] Arguments => new [] { QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return input.Where(e =>
(entityManager.TryGetComponent<TagComponent>(e, out var tag) &&
tag.Tags.Contains((string) arguments[0])) ^ isInverted);
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.EntityQuery<TagComponent>().Select(x => x.Owner), arguments,
isInverted, entityManager);
}
}
[RegisterBqlQuerySelector]
public sealed class AliveQuerySelector : BqlQuerySelector
{
public override string Token => "alive";
public override QuerySelectorArgument[] Arguments => Array.Empty<QuerySelectorArgument>();
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var mindSystem = entityManager.System<MindSystem>();
return input.Where(e =>
entityManager.TryGetComponent<MindContainerComponent>(e, out var mind)
&& mind.Mind != null
&& !mindSystem.IsCharacterDeadPhysically(mind.Mind));
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.EntityQuery<MindContainerComponent>().Select(x => x.Owner), arguments,
isInverted, entityManager);
}
}
[RegisterBqlQuerySelector]
public sealed class HasReagentQuerySelector : BqlQuerySelector
{
public override string Token => "hasreagent";
public override QuerySelectorArgument[] Arguments => new [] { QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var reagent = (string) arguments[0];
return input.Where(e =>
{
if (entityManager.TryGetComponent<SolutionContainerManagerComponent>(e, out var solutionContainerManagerComponent))
{
return solutionContainerManagerComponent.Solutions
.Any(solution => solution.Value.ContainsReagent(reagent)) ^ isInverted;
}
return isInverted;
});
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.EntityQuery<SolutionContainerManagerComponent>().Select(x => x.Owner), arguments,
isInverted, entityManager);
}
}
[RegisterBqlQuerySelector]
public sealed class ApcPoweredQuerySelector : BqlQuerySelector
{
public override string Token => "apcpowered";
public override QuerySelectorArgument[] Arguments => Array.Empty<QuerySelectorArgument>();
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return input.Where(e =>
entityManager.TryGetComponent<ApcPowerReceiverComponent>(e, out var apcPowerReceiver)
? apcPowerReceiver.Powered ^ isInverted
: isInverted);
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.EntityQuery<ApcPowerReceiverComponent>().Select(x => x.Owner), arguments,
isInverted, entityManager);
}
}
}
}

View File

@@ -19,18 +19,15 @@ using Content.Server.Preferences.Managers;
using Content.Server.ServerInfo;
using Content.Server.ServerUpdates;
using Content.Server.Voting.Managers;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Kitchen;
using Robust.Server;
using Robust.Server.Bql;
using Robust.Shared.Configuration;
using Robust.Server.ServerStatus;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Content.Server.Station.Systems;
using Content.Shared.Localizations;
namespace Content.Server.Entry
@@ -145,7 +142,6 @@ namespace Content.Server.Entry
IoCManager.Resolve<IGameMapManager>().Initialize();
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
IoCManager.Resolve<IBqlQueryManager>().DoAutoRegistrations();
IoCManager.Resolve<IBanManager>().Initialize();
}
}

View File

@@ -11,6 +11,7 @@ using Content.Server.GhostKick;
using Content.Server.Info;
using Content.Server.Maps;
using Content.Server.MoMMI;
using Content.Server.NewCon;
using Content.Server.NodeContainer.NodeGroups;
using Content.Server.Objectives;
using Content.Server.Objectives.Interfaces;
@@ -23,6 +24,7 @@ using Content.Server.Worldgen.Tools;
using Content.Shared.Administration.Logs;
using Content.Shared.Administration.Managers;
using Content.Shared.Kitchen;
using Robust.Shared.Toolshed;
namespace Content.Server.IoC
{

View File

@@ -0,0 +1,34 @@
using Content.Server.Administration;
using Content.Server.Administration.Managers;
using Content.Shared.Administration;
using Robust.Server.Player;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
namespace Content.Server.NewCon.Commands.AdminDebug;
[ToolshedCommand, AdminCommand(AdminFlags.Debug)]
public sealed class ACmdCommand : ToolshedCommand
{
[Dependency] private readonly IAdminManager _adminManager = default!;
[CommandImplementation("perms")]
public AdminFlags[]? Perms([PipedArgument] CommandSpec command)
{
var res = _adminManager.TryGetCommandFlags(command, out var flags);
if (res)
flags ??= Array.Empty<AdminFlags>();
return flags;
}
[CommandImplementation("caninvoke")]
public bool CanInvoke(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] CommandSpec command,
[CommandArgument] ValueRef<IPlayerSession> player
)
{
// Deliberately discard the error.
return ((IPermissionController) _adminManager).CheckInvokable(command, player.Evaluate(ctx), out var err);
}
}

View File

@@ -0,0 +1,62 @@
using System.Linq;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Verbs;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Server.NewCon.Commands.Verbs;
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class RunVerbAsCommand : ToolshedCommand
{
private SharedVerbSystem? _verb;
[CommandImplementation]
public IEnumerable<EntityUid> RunVerbAs(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input,
[CommandArgument] ValueRef<EntityUid> runner,
[CommandArgument] string verb
)
{
_verb ??= GetSys<SharedVerbSystem>();
verb = verb.ToLowerInvariant();
foreach (var i in input)
{
var runnerEid = runner.Evaluate(ctx);
if (EntityManager.Deleted(runnerEid) && runnerEid != default)
ctx.ReportError(new DeadEntity(runnerEid));
if (ctx.GetErrors().Any())
yield break;
var verbs = _verb.GetLocalVerbs(i, runnerEid, Verb.VerbTypes, true);
// if the "verb name" is actually a verb-type, try run any verb of that type.
var verbType = Verb.VerbTypes.FirstOrDefault(x => x.Name == verb);
if (verbType != null)
{
var verbTy = verbs.FirstOrDefault(v => v.GetType() == verbType);
if (verbTy != null)
{
_verb.ExecuteVerb(verbTy, runnerEid, i, forced: true);
yield return i;
}
}
foreach (var verbTy in verbs)
{
if (verbTy.Text.ToLowerInvariant() == verb)
{
_verb.ExecuteVerb(verbTy, runnerEid, i, forced: true);
yield return i;
}
}
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Bql;
using Content.Shared.Eui;
using Robust.Server.Player;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
namespace Content.Server.NewCon.Commands;
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class VisualizeCommand : ToolshedCommand
{
[Dependency] private readonly EuiManager _euiManager = default!;
[CommandImplementation]
public void VisualizeEntities(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> input
)
{
if (ctx.Session is null)
{
ctx.ReportError(new NotForServerConsoleError());
return;
}
var ui = new ToolshedVisualizeEui(
input.Select(e => (EntName(e), e)).ToArray()
);
_euiManager.OpenEui(ui, (IPlayerSession) ctx.Session);
_euiManager.QueueStateUpdate(ui);
}
}
internal sealed class ToolshedVisualizeEui : BaseEui
{
private readonly (string name, EntityUid entity)[] _entities;
public ToolshedVisualizeEui((string name, EntityUid entity)[] entities)
{
_entities = entities;
}
public override EuiStateBase GetNewState()
{
return new ToolshedVisualizeEuiState(_entities);
}
}

View File

@@ -0,0 +1,129 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Content.Shared.Roles;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Server.Station.Commands;
[ToolshedCommand, AdminCommand(AdminFlags.VarEdit)]
public sealed class JobsCommand : ToolshedCommand
{
private StationJobsSystem? _jobs;
[CommandImplementation("jobs")]
public IEnumerable<JobSlotRef> Jobs([PipedArgument] EntityUid station)
{
_jobs ??= GetSys<StationJobsSystem>();
foreach (var (job, _) in _jobs.GetJobs(station))
{
yield return new JobSlotRef(job, station, _jobs, EntityManager);
}
}
[CommandImplementation("jobs")]
public IEnumerable<JobSlotRef> Jobs([PipedArgument] IEnumerable<EntityUid> stations)
=> stations.SelectMany(Jobs);
[CommandImplementation("job")]
public JobSlotRef Job([PipedArgument] EntityUid station, [CommandArgument] Prototype<JobPrototype> job)
{
_jobs ??= GetSys<StationJobsSystem>();
return new JobSlotRef(job.Value.ID, station, _jobs, EntityManager);
}
[CommandImplementation("job")]
public IEnumerable<JobSlotRef> Job([PipedArgument] IEnumerable<EntityUid> stations, [CommandArgument] Prototype<JobPrototype> job)
=> stations.Select(x => Job(x, job));
[CommandImplementation("isinfinite")]
public bool IsInfinite([PipedArgument] JobSlotRef job, [CommandInverted] bool inverted)
=> job.Infinite() ^ inverted;
[CommandImplementation("isinfinite")]
public IEnumerable<bool> IsInfinite([PipedArgument] IEnumerable<JobSlotRef> jobs, [CommandInverted] bool inverted)
=> jobs.Select(x => IsInfinite(x, inverted));
[CommandImplementation("adjust")]
public JobSlotRef Adjust(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] JobSlotRef @ref,
[CommandArgument] ValueRef<int> by
)
{
_jobs ??= GetSys<StationJobsSystem>();
_jobs.TryAdjustJobSlot(@ref.Station, @ref.Job, by.Evaluate(ctx), true, true);
return @ref;
}
[CommandImplementation("adjust")]
public IEnumerable<JobSlotRef> Adjust(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<JobSlotRef> @ref,
[CommandArgument] ValueRef<int> by
)
=> @ref.Select(x => Adjust(ctx, x, by));
[CommandImplementation("set")]
public JobSlotRef Set(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] JobSlotRef @ref,
[CommandArgument] ValueRef<int> by
)
{
_jobs ??= GetSys<StationJobsSystem>();
_jobs.TrySetJobSlot(@ref.Station, @ref.Job, by.Evaluate(ctx), true);
return @ref;
}
[CommandImplementation("set")]
public IEnumerable<JobSlotRef> Set(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<JobSlotRef> @ref,
[CommandArgument] ValueRef<int> by
)
=> @ref.Select(x => Set(ctx, x, by));
[CommandImplementation("amount")]
public int Amount(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] JobSlotRef @ref
)
{
_jobs ??= GetSys<StationJobsSystem>();
_jobs.TryGetJobSlot(@ref.Station, @ref.Job, out var slots);
return (int)(slots ?? 0);
}
[CommandImplementation("amount")]
public IEnumerable<int> Amount(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<JobSlotRef> @ref
)
=> @ref.Select(x => Amount(ctx, x));
}
// Used for Toolshed queries.
public readonly record struct JobSlotRef(string Job, EntityUid Station, StationJobsSystem Jobs, IEntityManager EntityManager)
{
public override string ToString()
{
if (!Jobs.TryGetJobSlot(Station, Job, out var slot))
{
return $"{EntityManager.ToPrettyString(Station)} job {Job} : (not a slot)";
}
return $"{EntityManager.ToPrettyString(Station)} job {Job} : {slot?.ToString() ?? "infinite"}";
}
public bool Infinite()
{
return Jobs.TryGetJobSlot(Station, Job, out var slot) && slot is null;
}
}

View File

@@ -0,0 +1,126 @@
using System.Diagnostics;
using System.Linq;
using Content.Server.Administration;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Administration;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Utility;
namespace Content.Server.Station.Commands;
[ToolshedCommand, AdminCommand(AdminFlags.Admin)]
public sealed class StationsCommand : ToolshedCommand
{
private StationSystem? _station;
[CommandImplementation("list")]
public IEnumerable<EntityUid> List()
{
_station ??= GetSys<StationSystem>();
return _station.GetStationsSet();
}
[CommandImplementation("get")]
public EntityUid Get([CommandInvocationContext] IInvocationContext ctx)
{
_station ??= GetSys<StationSystem>();
var set = _station.GetStationsSet();
if (set.Count > 1 || set.Count == 0)
ctx.ReportError(new OnlyOneStationsError());
return set.FirstOrDefault();
}
[CommandImplementation("getowningstation")]
public IEnumerable<EntityUid?> GetOwningStation([PipedArgument] IEnumerable<EntityUid> input)
=> input.Select(GetOwningStation);
[CommandImplementation("getowningstation")]
public EntityUid? GetOwningStation([PipedArgument] EntityUid input)
{
_station ??= GetSys<StationSystem>();
return _station.GetOwningStation(input);
}
[CommandImplementation("largestgrid")]
public EntityUid? LargestGrid([PipedArgument] EntityUid input)
{
_station ??= GetSys<StationSystem>();
return _station.GetLargestGrid(Comp<StationDataComponent>(input));
}
[CommandImplementation("largestgrid")]
public IEnumerable<EntityUid?> LargestGrid([PipedArgument] IEnumerable<EntityUid> input)
=> input.Select(LargestGrid);
[CommandImplementation("grids")]
public IEnumerable<EntityUid> Grids([PipedArgument] EntityUid input)
=> Comp<StationDataComponent>(input).Grids;
[CommandImplementation("grids")]
public IEnumerable<EntityUid> Grids([PipedArgument] IEnumerable<EntityUid> input)
=> input.SelectMany(Grids);
[CommandImplementation("config")]
public StationConfig? Config([PipedArgument] EntityUid input)
=> Comp<StationDataComponent>(input).StationConfig;
[CommandImplementation("config")]
public IEnumerable<StationConfig?> Config([PipedArgument] IEnumerable<EntityUid> input)
=> input.Select(Config);
[CommandImplementation("addgrid")]
public void AddGrid(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<EntityUid> grid
)
{
_station ??= GetSys<StationSystem>();
_station.AddGridToStation(input, grid.Evaluate(ctx));
}
[CommandImplementation("rmgrid")]
public void RmGrid(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<EntityUid> grid
)
{
_station ??= GetSys<StationSystem>();
_station.RemoveGridFromStation(input, grid.Evaluate(ctx));
}
[CommandImplementation("rename")]
public void Rename([CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string> name
)
{
_station ??= GetSys<StationSystem>();
_station.RenameStation(input, name.Evaluate(ctx)!);
}
}
public record struct OnlyOneStationsError : IConError
{
public FormattedMessage DescribeInner()
{
return FormattedMessage.FromMarkup("This command doesn't function if there is more than one or no stations, explicitly specify a station with the ent command or similar.");
}
public string? Expression { get; set; }
public Vector2i? IssueSpan { get; set; }
public StackTrace? Trace { get; set; }
}