Skip to content

Commit db89b39

Browse files
authored
Conditions now can be used by other plugins (#210)
* Conditions now can be used by other plugins To make other plugins to be able extend you need 1) Define class that is marked [ConditionModule] 2) Add method in that class is marked by [Condition("name", helpText: "tooltip")] 3) Method should return bool, and accept MyCubeGrid. It may also accept 1 extra param (string, double, int ...) * Added new commands (optimization) hastype-fast/notype-fast hassubtype-fast/nosubtype-fast Same as without "-fast", but also support multiple types/subtypes * Parallel iterate
1 parent 55b53d6 commit db89b39

9 files changed

Lines changed: 580 additions & 411 deletions

‎Essentials/Commands/CleanupModule.cs‎

Lines changed: 4 additions & 410 deletions
Large diffs are not rendered by default.

‎Essentials/Conditions/Condition.cs‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Reflection;
3+
using Sandbox.Game.Entities;
4+
using Torch.Commands;
5+
6+
namespace Essentials.Conditions
7+
{
8+
public class Condition
9+
{
10+
public string Command;
11+
public string InvertCommand;
12+
public string HelpText;
13+
private MethodInfo _method;
14+
public readonly ParameterInfo Parameter;
15+
16+
public Condition(MethodInfo evalMethod, ConditionAttribute attribute)
17+
{
18+
Command = attribute.Command;
19+
InvertCommand = attribute.InvertCommand;
20+
HelpText = attribute.HelpText;
21+
_method = evalMethod;
22+
if (_method.ReturnType != typeof(bool))
23+
throw new TypeLoadException("Condition does not return a bool!");
24+
var p = _method.GetParameters();
25+
if (p.Length < 1 || p[0].ParameterType != typeof(MyCubeGrid))
26+
throw new TypeLoadException("Condition does not accept MyCubeGrid as first parameter");
27+
if (p.Length > 2)
28+
throw new TypeLoadException("Condition can only have two parameters");
29+
if (p.Length == 1)
30+
Parameter = null;
31+
else
32+
Parameter = p[1];
33+
}
34+
35+
public bool? Evaluate(MyCubeGrid grid, string arg, bool invert, CommandContext context)
36+
{
37+
bool result;
38+
if (!string.IsNullOrEmpty(arg) && Parameter == null)
39+
{
40+
context.Respond($"Condition does not accept an argument. Cannot continue!");
41+
return null;
42+
}
43+
if (string.IsNullOrEmpty(arg) && Parameter != null && !Parameter.HasDefaultValue)
44+
{
45+
context.Respond($"Condition requires an argument! {Parameter.ParameterType.Name}: {Parameter.Name} Not supplied, cannot continue!");
46+
return null;
47+
}
48+
if (Parameter != null && !string.IsNullOrEmpty(arg))
49+
{
50+
if (!arg.TryConvert(Parameter.ParameterType, out object val))
51+
{
52+
context.Respond($"Could not parse argument!");
53+
return null;
54+
}
55+
56+
result = (bool)_method.Invoke(null, new[] { grid, val });
57+
}
58+
else
59+
{
60+
result = (bool)_method.Invoke(null, new object[] { grid });
61+
}
62+
63+
return result != invert;
64+
}
65+
}
66+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace Essentials
4+
{
5+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
6+
public sealed class ConditionAttribute : Attribute
7+
{
8+
public string Command;
9+
public string InvertCommand;
10+
public string HelpText;
11+
12+
public ConditionAttribute(string command, string invertCommand = null, string helpText = null)
13+
{
14+
Command = command;
15+
InvertCommand = invertCommand;
16+
HelpText = helpText;
17+
}
18+
}
19+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace Essentials
4+
{
5+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
6+
public sealed class ConditionModule : Attribute
7+
{
8+
9+
}
10+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Threading.Tasks;
6+
using Essentials.Conditions;
7+
using Sandbox.Game.Entities;
8+
using Torch.Commands;
9+
10+
namespace Essentials.Commands
11+
{
12+
public static class ConditionsChecker
13+
{
14+
private static List<Condition> _conditionLookup;
15+
16+
public static void Init()
17+
{
18+
_conditionLookup = new List<Condition>();
19+
20+
var types = AppDomain.CurrentDomain.GetAssemblies()
21+
.SelectMany((x) =>
22+
{
23+
try
24+
{
25+
return x.GetTypes();
26+
}
27+
catch (Exception e) // ignored
28+
{
29+
return new Type[0];
30+
}
31+
}).Where(t => t.IsDefined(typeof(ConditionModule)));
32+
33+
foreach (var type in types)
34+
{
35+
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
36+
foreach (var m in methods)
37+
{
38+
var a = m.GetCustomAttribute<ConditionAttribute>();
39+
if (a == null)
40+
continue;
41+
42+
var c = new Condition(m, a);
43+
44+
_conditionLookup.Add(c);
45+
}
46+
}
47+
}
48+
49+
public static List<Condition> GetAllConditions()
50+
{
51+
return _conditionLookup;
52+
}
53+
54+
public static IEnumerable<MyCubeGrid> ScanConditions(CommandContext context, IReadOnlyList<string> args)
55+
{
56+
var conditions = new List<Func<MyCubeGrid, bool?>>();
57+
58+
for (var i = 0; i < args.Count; i++)
59+
{
60+
string parameter;
61+
if (i + 1 >= args.Count)
62+
{
63+
parameter = null;
64+
}
65+
else
66+
{
67+
parameter = args[i + 1];
68+
}
69+
70+
var arg = args[i];
71+
72+
if (parameter != null)
73+
{
74+
//parameter is the name of a command. Assume this command requires no parameters
75+
if (_conditionLookup.Any(c => parameter.Equals(c.Command, StringComparison.CurrentCultureIgnoreCase) || parameter.Equals(c.InvertCommand, StringComparison.CurrentCultureIgnoreCase)))
76+
{
77+
parameter = null;
78+
}
79+
//next string is a parameter, so pass it to the condition and skip it next loop
80+
else
81+
i++;
82+
}
83+
84+
bool found = false;
85+
86+
foreach (var condition in _conditionLookup)
87+
{
88+
if (arg.Equals(condition.Command, StringComparison.CurrentCultureIgnoreCase))
89+
{
90+
conditions.Add(g => condition.Evaluate(g, parameter, false, context));
91+
found = true;
92+
break;
93+
}
94+
else if (arg.Equals(condition.InvertCommand, StringComparison.CurrentCultureIgnoreCase))
95+
{
96+
conditions.Add(g => condition.Evaluate(g, parameter, true, context));
97+
found = true;
98+
break;
99+
}
100+
}
101+
if (!found)
102+
{
103+
context.Respond($"Unknown argument '{arg}'");
104+
return new List<MyCubeGrid>();
105+
}
106+
}
107+
108+
//default scan to find grids without pilots
109+
if (!args.Contains("haspilot", StringComparer.CurrentCultureIgnoreCase))
110+
conditions.Add(g => !ConditionsImplementations.Piloted(g));
111+
112+
113+
var resultList = new List<MyCubeGrid>();
114+
Parallel.ForEach(MyCubeGridGroups.Static.Logical.Groups, (group) =>
115+
{
116+
//if (group.Nodes.All(grid => conditions.TrueForAll(func => func(grid.NodeData))))
117+
bool res = true;
118+
foreach (var node in group.Nodes)
119+
{
120+
if (node.NodeData.Projector != null)
121+
continue;
122+
123+
foreach (var c in conditions)
124+
{
125+
bool? r = c.Invoke(node.NodeData);
126+
if (r == null)
127+
{
128+
return;
129+
}
130+
131+
if (r == true)
132+
{
133+
continue;
134+
}
135+
136+
res = false;
137+
break;
138+
}
139+
140+
if (!res)
141+
{
142+
break;
143+
}
144+
}
145+
146+
if (res)
147+
{
148+
lock (resultList)
149+
{
150+
foreach (var grid in group.Nodes.Where(x => x.NodeData.Projector == null))
151+
{
152+
resultList.Add(grid.NodeData);
153+
}
154+
}
155+
}
156+
157+
});
158+
159+
return resultList;
160+
}
161+
162+
}
163+
}

0 commit comments

Comments
 (0)