Introduce BQLv2, part 1 (#2170)

This commit is contained in:
moonheart08
2021-10-31 14:23:13 -05:00
committed by GitHub
parent 0a59079a4a
commit 9fc95591d9
12 changed files with 771 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Pidgin;
using Robust.Shared.GameObjects;
using static Pidgin.Parser;
using static Pidgin.Parser<char>;
namespace Robust.Server.Bql
{
public partial class BqlQueryManager
{
private readonly Dictionary<Type, Parser<char, BqlQuerySelectorParsed>> _parsers = new();
private Parser<char, BqlQuerySelectorParsed> _allQuerySelectors = default!;
private Parser<char, (IEnumerable<BqlQuerySelectorParsed>, string)> SimpleQuery => Parser.Map((en, _, rest) => (en, rest), SkipWhitespaces.Then(_allQuerySelectors).Many(), String("do").Then(SkipWhitespaces), Any.ManyString());
private static Parser<char, string> Word => OneOf(LetterOrDigit, Char('_')).ManyString();
private static Parser<char, object> Objectify<T>(Parser<char, T> inp)
{
return Parser.Map(x => (object) x!, inp);
}
private struct SubstitutionData
{
public string Name;
public SubstitutionData(string name)
{
Name = name;
}
}
private static Parser<char, SubstitutionData> Substitution =>
Try(Char('$').Then(OneOf(Uppercase, Char('_')).ManyString()))
.MapWithInput((x, _) => new SubstitutionData(x.ToString()));
private static Parser<char, int> Integer =>
Try(Int(10));
private static Parser<char, object> SubstitutableInteger =>
Objectify(Integer).Or(Objectify(Try(Substitution)));
private static Parser<char, double> Float =>
Try(Real);
private static Parser<char, object> SubstitutableFloat =>
Objectify(Float).Or(Objectify(Try(Substitution)));
private static Parser<char, double> Percentage =>
Try(Real).Before(Char('%'));
private static Parser<char, object> SubstitutablePercentage =>
Objectify(Percentage).Or(Objectify(Try(Substitution)));
private static Parser<char, EntityUid> EntityId =>
Try(Parser.Map(x => new EntityUid(x), Int(10)));
private static Parser<char, object> SubstitutableEntityId =>
Objectify(EntityId).Or(Objectify(Try(Substitution)));
private Parser<char, Type> Component =>
Try(Parser.Map(t => _componentFactory.GetRegistration(t).Type, Word));
private Parser<char, object> SubstitutableComponent =>
Objectify(Component).Or(Objectify(Try(Substitution)));
private static Parser<char, string> QuotedString =>
OneOf(Try(Char('"').Then(OneOf(new []
{
AnyCharExcept("\"")
}).ManyString().Before(Char('"')))), Try(Word));
private static Parser<char, object> SubstitutableString =>
Objectify(QuotedString).Or(Objectify(Try(Substitution)));
// thing to make sure it all compiles.
[UsedImplicitly]
private Parser<char, object> TypeSystemCheck =>
OneOf(new[]
{
Objectify(Integer),
Objectify(Percentage),
Objectify(EntityId),
Objectify(Component),
Objectify(Float),
Objectify(QuotedString)
});
private Parser<char, BqlQuerySelectorParsed> BuildBqlQueryParser(BqlQuerySelector inst)
{
if (inst.Arguments.Length == 0)
{
return Parser.Map(_ => new BqlQuerySelectorParsed(new List<object>(), inst.Token, false), SkipWhitespaces);
}
List<Parser<char, object>> argsParsers = new();
foreach (var (arg, _) in inst.Arguments.Select((x, i) => (x, i)))
{
List<Parser<char, object>> choices = new();
if ((arg & QuerySelectorArgument.String) == QuerySelectorArgument.String)
{
choices.Add(Try(SubstitutableString.Before(SkipWhitespaces).Labelled("string argument")));
}
if ((arg & QuerySelectorArgument.Component) == QuerySelectorArgument.Component)
{
choices.Add(Try(SubstitutableComponent.Before(SkipWhitespaces).Labelled("component argument")));
}
if ((arg & QuerySelectorArgument.EntityId) == QuerySelectorArgument.EntityId)
{
choices.Add(Try(SubstitutableEntityId.Before(SkipWhitespaces).Labelled("entity ID argument")));
}
if ((arg & QuerySelectorArgument.Integer) == QuerySelectorArgument.Integer)
{
choices.Add(Try(SubstitutableInteger.Before(SkipWhitespaces).Labelled("integer argument")));
}
if ((arg & QuerySelectorArgument.Percentage) == QuerySelectorArgument.Percentage)
{
choices.Add(Try(SubstitutablePercentage.Before(SkipWhitespaces).Labelled("percentage argument")));
}
if ((arg & QuerySelectorArgument.Float) == QuerySelectorArgument.Float)
{
choices.Add(Try(SubstitutableFloat.Before(SkipWhitespaces).Labelled("float argument")));
}
argsParsers.Add(OneOf(choices));
}
Parser<char, List<object>> finalParser = argsParsers[0].Map(x => new List<object> { x });
for (var i = 1; i < argsParsers.Count; i++)
{
finalParser = finalParser.Then(argsParsers[i], (list, o) =>
{
list.Add(o);
return list;
}).Labelled("arguments");
}
return Parser.Map(args => new BqlQuerySelectorParsed(args, inst.Token, false), finalParser);
}
private void DoParserSetup()
{
foreach (var inst in _instances)
{
_parsers.Add(inst.GetType(), BuildBqlQueryParser(inst));
}
_allQuerySelectors = Parser.Map((a,b) => (a,b), Try(String("not").Before(Char(' '))).Optional(), OneOf(_instances.Select(x =>
Try(String(x.Token).Before(Char(' '))))).Then(tok =>
_parsers[_queriesByToken[tok].GetType()])
).Map(pair =>
{
pair.b.Inverted = pair.a.HasValue;
return pair.b;
});
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Pidgin;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Bql
{
public partial class BqlQueryManager
{
public (IEnumerable<EntityUid>, string) SimpleParseAndExecute(string query)
{
var parsed = SimpleQuery.Parse(query);
if (parsed.Success)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
var selectors = parsed.Value.Item1.ToArray();
if (selectors.Length == 0)
{
return (entityManager.GetEntityUids(), parsed.Value.Item2);
}
var entities = _queriesByToken[selectors[0].Token]
.DoInitialSelection(selectors[0].Arguments, selectors[0].Inverted, entityManager);
foreach (var sel in selectors[1..])
{
entities = _queriesByToken[sel.Token].DoSelection(entities, sel.Arguments, sel.Inverted, entityManager);
}
return (entities, parsed.Value.Item2);
}
else
{
throw new Exception(parsed.Error!.RenderErrorMessage());
}
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Server.Bql
{
public partial class BqlQueryManager : IBqlQueryManager
{
private readonly IReflectionManager _reflectionManager;
private readonly IComponentFactory _componentFactory;
private readonly List<BqlQuerySelector> _instances = new();
private readonly Dictionary<string, BqlQuerySelector> _queriesByToken = new();
public BqlQueryManager()
{
_reflectionManager = IoCManager.Resolve<IReflectionManager>();
_componentFactory = IoCManager.Resolve<IComponentFactory>();
}
/// <summary>
/// Automatically registers all query selectors with the parser/executor.
/// </summary>
public void DoAutoRegistrations()
{
foreach (var type in _reflectionManager.FindTypesWithAttribute<RegisterBqlQuerySelectorAttribute>())
{
RegisterClass(type);
}
DoParserSetup();
}
/// <summary>
/// Internally registers the given <see cref="BqlQuerySelector"/>.
/// </summary>
/// <param name="bqlQuerySelector">The selector to register</param>
private void RegisterClass(Type bqlQuerySelector)
{
DebugTools.Assert(bqlQuerySelector.BaseType == typeof(BqlQuerySelector));
var inst = (BqlQuerySelector)Activator.CreateInstance(bqlQuerySelector)!;
_instances.Add(inst);
_queriesByToken.Add(inst.Token, inst);
}
}
}

View File

@@ -0,0 +1,329 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Robust.Server.Bql
{
[RegisterBqlQuerySelector]
public class WithQuerySelector : BqlQuerySelector
{
public override string Token => "with";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.Component };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var comp = (Type) arguments[0];
return input.Where(x => entityManager.HasComponent(x, comp) ^ isInverted);
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
if (isInverted)
{
return base.DoInitialSelection(arguments, isInverted, entityManager);
}
return entityManager.GetAllComponents((Type) arguments[0])
.Select(x => x.OwnerUid);
}
}
[RegisterBqlQuerySelector]
public class NamedQuerySelector : BqlQuerySelector
{
public override string Token => "named";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var r = new Regex("^" + (string) arguments[0] + "$");
return input.Where(e =>
{
if (entityManager.TryGetComponent<MetaDataComponent>(e, out var metaDataComponent))
return r.IsMatch(metaDataComponent.EntityName) ^ isInverted;
return isInverted;
});
}
}
[RegisterBqlQuerySelector]
public class ParentedToQuerySelector : BqlQuerySelector
{
public override string Token => "parentedto";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.EntityId };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var uid = (EntityUid) arguments[0];
return input.Where(e => (entityManager.TryGetComponent<ITransformComponent>(e, out var transform) &&
transform.Parent?.OwnerUid == uid) ^ isInverted);
}
}
[RegisterBqlQuerySelector]
public class RecursiveParentedToQuerySelector : BqlQuerySelector
{
public override string Token => "rparentedto";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.EntityId };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var uid = (EntityUid) arguments[0];
return input.Where(e =>
{
if (!entityManager.TryGetComponent<ITransformComponent>(e, out var transform))
return isInverted;
var cur = transform;
while (cur.ParentUid != EntityUid.Invalid)
{
if ((cur.ParentUid == uid) ^ isInverted)
return true;
if (cur.Parent is null)
return false;
cur = cur.Parent;
}
return false;
});
}
}
[RegisterBqlQuerySelector]
public class ChildrenQuerySelector : BqlQuerySelector
{
public override string Token => "children";
public override QuerySelectorArgument[] Arguments => Array.Empty<QuerySelectorArgument>();
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return input.SelectMany(e =>
{
return entityManager.TryGetComponent<ITransformComponent>(e, out var transform)
? transform.Children.Select(y => y.OwnerUid)
: new List<EntityUid>();
});
}
}
[RegisterBqlQuerySelector]
public class RecursiveChildrenQuerySelector : BqlQuerySelector
{
public override string Token => "rchildren";
public override QuerySelectorArgument[] Arguments => Array.Empty<QuerySelectorArgument>();
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments,
bool isInverted, IEntityManager entityManager)
{
IEnumerable<EntityUid> toSearch = input;
List<IEnumerable<EntityUid>> children = new List<IEnumerable<EntityUid>>();
while (true)
{
var doing = toSearch.Where(entityManager.HasComponent<ITransformComponent>).Select(entityManager.GetComponent<ITransformComponent>).ToArray();
var search = doing.SelectMany(x => x.Children.Select(y => y.Owner));
if (!search.Any())
break;
toSearch = doing.SelectMany(x => x.Children.Select(y => y.OwnerUid)).Where(x => x != EntityUid.Invalid);
children.Add(doing.SelectMany(x => x.Children.Select(y => y.OwnerUid)).Where(x => x != EntityUid.Invalid));
}
return children.SelectMany(x => x);
}
}
[RegisterBqlQuerySelector]
public class ParentQuerySelector : BqlQuerySelector
{
public override string Token => "parent";
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(entityManager.HasComponent<ITransformComponent>)
.Select(e => entityManager.GetComponent<ITransformComponent>(e).OwnerUid)
.Distinct();
}
public override IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.EntityQuery<ITransformComponent>().Select(x => x.OwnerUid), arguments,
isInverted, entityManager);
}
}
[RegisterBqlQuerySelector]
public class AboveQuerySelector : BqlQuerySelector
{
public override string Token => "above";
public override QuerySelectorArgument[] Arguments => new [] { QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var tileTy = tileDefinitionManager[(string) arguments[0]];
var entity = IoCManager.Resolve<IEntityManager>();
var map = IoCManager.Resolve<IMapManager>();
if (tileTy.TileId == 0)
{
return input.Where(e => entityManager.TryGetComponent<ITransformComponent>(e, out var transform) && (transform.Coordinates.GetGridId(entity) == GridId.Invalid) ^ isInverted);
}
else
{
return input.Where(e =>
{
if (!entityManager.TryGetComponent<ITransformComponent>(e, out var transform)) return isInverted;
var gridId = transform.Coordinates.GetGridId(entity);
if (gridId == GridId.Invalid)
return isInverted;
var grid = map.GetGrid(gridId);
return (grid.GetTileRef(transform.Coordinates).Tile.TypeId == tileTy.TileId) ^ isInverted;
});
}
}
}
[RegisterBqlQuerySelector]
// ReSharper disable once InconsistentNaming the name is correct shut up
public class OnGridQuerySelector : BqlQuerySelector
{
public override string Token => "ongrid";
public override QuerySelectorArgument[] Arguments => new [] { QuerySelectorArgument.Integer };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var grid = new GridId((int) arguments[0]);
return input.Where(e => (entityManager.TryGetComponent<ITransformComponent>(e, out var transform) && transform.GridID == grid) ^ isInverted);
}
}
[RegisterBqlQuerySelector]
// ReSharper disable once InconsistentNaming the name is correct shut up
public class OnMapQuerySelector : BqlQuerySelector
{
public override string Token => "onmap";
public override QuerySelectorArgument[] Arguments => new [] { QuerySelectorArgument.Integer };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var map = new MapId((int) arguments[0]);
return input.Where(e => (entityManager.TryGetComponent<ITransformComponent>(e, out var transform) && transform.MapID == map) ^ isInverted);
}
}
[RegisterBqlQuerySelector]
public class PrototypedQuerySelector : BqlQuerySelector
{
public override string Token => "prototyped";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var name = (string) arguments[0];
return input.Where(e => (entityManager.TryGetComponent<MetaDataComponent>(e, out var metaData) && metaData.EntityPrototype?.ID == name) ^ isInverted);
}
}
[RegisterBqlQuerySelector]
public class RecursivePrototypedQuerySelector : BqlQuerySelector
{
public override string Token => "rprototyped";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.String };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var name = (string) arguments[0];
return input.Where(e =>
{
if (!entityManager.TryGetComponent<MetaDataComponent>(e, out var metaData))
return isInverted;
if ((metaData.EntityPrototype?.ID == name) ^ isInverted)
return true;
return (metaData.EntityPrototype?.Parent == name) ^ isInverted; // Damn, can't actually do recursive check here.
});
}
}
[RegisterBqlQuerySelector]
public class SelectQuerySelector : BqlQuerySelector
{
public override string Token => "select";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.Integer | QuerySelectorArgument.Percentage };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
if (arguments[0] is int)
{
var inp = input.OrderBy(_ => Guid.NewGuid()).ToArray();
var taken = (int) arguments[0];
if (isInverted)
taken = Math.Max(0, inp.Length - taken);
return inp.Take(taken);
}
var enumerable = input.OrderBy(_ => Guid.NewGuid()).ToArray();
var amount = isInverted
? (int) Math.Floor(enumerable.Length * Math.Clamp(1 - (double) arguments[0], 0, 1))
: (int) Math.Floor(enumerable.Length * Math.Clamp((double) arguments[0], 0, 1));
return enumerable.Take(amount);
}
}
[RegisterBqlQuerySelector]
public class NearQuerySelector : BqlQuerySelector
{
public override string Token => "near";
public override QuerySelectorArgument[] Arguments => new []{ QuerySelectorArgument.Float };
public override IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input, IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
var radius = (float)(double)arguments[0];
var entityLookup = IoCManager.Resolve<IEntityLookup>();
//BUG: GetEntitiesInRange effectively uses manhattan distance. This is not intended, near is supposed to be circular.
return input.Where(entityManager.HasComponent<ITransformComponent>)
.SelectMany(e =>
entityLookup.GetEntitiesInRange(entityManager.GetComponent<ITransformComponent>(e).Coordinates,
radius))
.Select(x => x.Uid) // Sloth's fault.
.Distinct();
}
}
[RegisterBqlQuerySelector]
// ReSharper disable once InconsistentNaming the name is correct shut up
public class anchoredQuerySelector : BqlQuerySelector
{
public override string Token => "anchored";
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<ITransformComponent>(e, out var transform) && transform.Anchored) ^ isInverted);
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Bql
{
[Flags]
[PublicAPI]
public enum QuerySelectorArgument
{
Integer = 0b00000001,
Float = 0b00000010,
String = 0b00000100,
Percentage = 0b00001000,
Component = 0b00010000,
//SubQuery = 0b00100000,
EntityId = 0b01000000,
}
[PublicAPI]
public abstract class BqlQuerySelector
{
/// <summary>
/// The token name for the given QuerySelector, for example `when`.
/// </summary>
public virtual string Token => throw new NotImplementedException();
/// <summary>
/// Arguments for the given QuerySelector, presented as "what arguments are permitted in what spot".
/// </summary>
public virtual QuerySelectorArgument[] Arguments => throw new NotImplementedException();
/// <summary>
/// Performs a transform over it's input entity list, whether that be filtering (selecting) or expanding the
/// input on some criteria like what entities are nearby.
/// </summary>
/// <param name="input">Input entity list.</param>
/// <param name="arguments">Parsed selector arguments.</param>
/// <param name="isInverted">Whether the query is inverted.</param>
/// <param name="entityManager">The entity manager.</param>
/// <returns>New list of entities</returns>
/// <exception cref="NotImplementedException">someone is a moron if this happens.</exception>
public abstract IEnumerable<EntityUid> DoSelection(IEnumerable<EntityUid> input,
IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager);
/// <summary>
/// Performs selection as the first selector in the query. Allows for optimizing when you can be more efficient
/// than just querying every entity.
/// </summary>
/// <param name="arguments"></param>
/// <param name="isInverted"></param>
/// <param name="entityManager"></param>
/// <returns></returns>
public virtual IEnumerable<EntityUid> DoInitialSelection(IReadOnlyList<object> arguments, bool isInverted, IEntityManager entityManager)
{
return DoSelection(entityManager.GetEntityUids(), arguments, isInverted, entityManager);
}
[UsedImplicitly]
protected BqlQuerySelector() {}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace Robust.Server.Bql
{
public struct BqlQuerySelectorParsed
{
public List<object> Arguments;
public string Token;
public bool Inverted;
public BqlQuerySelectorParsed(List<object> arguments, string token, bool inverted)
{
Arguments = arguments;
Token = token;
Inverted = inverted;
}
}
}

View File

@@ -0,0 +1,73 @@
using System.Globalization;
using System.Linq;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.Server.Bql
{
public class ForAllCommand : IConsoleCommand
{
public string Command => "forall";
public string Description => "Runs a command over all entities with a given component";
public string Help => "Usage: forall <bql query> do <command...>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 2)
{
shell.WriteLine(Help);
return;
}
var queryManager = IoCManager.Resolve<IBqlQueryManager>();
var entityManager = IoCManager.Resolve<IEntityManager>();
var (entities, rest) = queryManager.SimpleParseAndExecute(argStr[6..]);
foreach (var ent in entities.ToList())
{
var cmds = SubstituteEntityDetails(shell, entityManager.GetEntity(ent), rest).Split(";");
foreach (var cmd in cmds)
{
shell.ExecuteCommand(cmd);
}
}
}
// This will be refactored out soon.
private static string SubstituteEntityDetails(IConsoleShell shell, IEntity ent, string ruleString)
{
// gross, is there a better way to do this?
ruleString = ruleString.Replace("$ID", ent.Uid.ToString());
ruleString = ruleString.Replace("$WX",
ent.Transform.WorldPosition.X.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$WY",
ent.Transform.WorldPosition.Y.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$LX",
ent.Transform.LocalPosition.X.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$LY",
ent.Transform.LocalPosition.Y.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$NAME", ent.Name);
if (shell.Player is IPlayerSession player)
{
if (player.AttachedEntity != null)
{
var p = player.AttachedEntity;
ruleString = ruleString.Replace("$PID", ent.Uid.ToString());
ruleString = ruleString.Replace("$PWX",
p.Transform.WorldPosition.X.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$PWY",
p.Transform.WorldPosition.Y.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$PLX",
p.Transform.LocalPosition.X.ToString(CultureInfo.InvariantCulture));
ruleString = ruleString.Replace("$PLY",
p.Transform.LocalPosition.Y.ToString(CultureInfo.InvariantCulture));
}
}
return ruleString;
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
namespace Robust.Server.Bql
{
public interface IBqlQueryManager
{
public (IEnumerable<EntityUid>, string) SimpleParseAndExecute(string query);
void DoAutoRegistrations();
}
}

View File

@@ -0,0 +1,14 @@
using System;
using JetBrains.Annotations;
namespace Robust.Server.Bql
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[BaseTypeRequired(typeof(BqlQuerySelector))]
[MeansImplicitUse]
[PublicAPI]
public class RegisterBqlQuerySelectorAttribute : Attribute
{
}
}

View File

@@ -1,3 +1,4 @@
using Robust.Server.Bql;
using Robust.Server.Console;
using Robust.Server.DataMetrics;
using Robust.Server.Debugging;
@@ -73,6 +74,7 @@ namespace Robust.Server
IoCManager.Register<IMetricsManager, MetricsManager>();
IoCManager.Register<IAuthManager, AuthManager>();
IoCManager.Register<IPhysicsManager, PhysicsManager>();
IoCManager.Register<IBqlQueryManager, BqlQueryManager>();
}
}
}

View File

@@ -224,6 +224,8 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public IEnumerable<IEntity> GetEntities() => Entities.Values;
public IEnumerable<EntityUid> GetEntityUids() => Entities.Keys;
/// <summary>
/// Shuts-down and removes given Entity. This is also broadcast to all clients.
/// </summary>

View File

@@ -86,6 +86,12 @@ namespace Robust.Shared.GameObjects
/// <returns></returns>
IEnumerable<IEntity> GetEntities();
/// <summary>
/// Returns all entities by uid
/// </summary>
/// <returns></returns>
IEnumerable<EntityUid> GetEntityUids();
public void QueueDeleteEntity(IEntity entity);
public void QueueDeleteEntity(EntityUid uid);