Client LuaCsForBarotrauma
RegExFindComponent.cs
1 using System;
2 using System.Text.RegularExpressions;
3 
5 {
7  {
8  private static readonly TimeSpan timeout = TimeSpan.FromMilliseconds(1);
9 
10  private string expression;
11 
12  private string receivedSignal;
13  private string previousReceivedSignal;
14 
15  private bool previousResult;
16  private GroupCollection previousGroups;
17 
18  private Regex regex;
19 
20  private bool nonContinuousOutputSent;
21 
22  private int maxOutputLength;
23  [Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")]
24  public int MaxOutputLength
25  {
26  get { return maxOutputLength; }
27  set
28  {
29  maxOutputLength = Math.Max(value, 0);
30  }
31  }
32 
33  private string output;
34 
35  [InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)]
36  public string Output
37  {
38  get { return output; }
39  set
40  {
41  if (value == null) { return; }
42  output = value;
43  if (output.Length > MaxOutputLength && (item.Submarine == null || !item.Submarine.Loading))
44  {
45  output = output.Substring(0, MaxOutputLength);
46  }
47  }
48  }
49 
50  [InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component output a value of a capture group instead of a constant signal.", alwaysUseInstanceValues: true)]
51  public bool UseCaptureGroup { get; set; }
52 
53  [InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component output the value of a capture group even if it's empty?", alwaysUseInstanceValues: true)]
54  public bool OutputEmptyCaptureGroup { get; set; }
55 
56  [InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal does not match the regular expression.", alwaysUseInstanceValues: true)]
57  public string FalseOutput { get; set; }
58 
59  [InGameEditable, Serialize(true, IsPropertySaveable.Yes, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.", alwaysUseInstanceValues: true)]
60  public bool ContinuousOutput { get; set; }
61 
62  [InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The regular expression used to check the incoming signals.", alwaysUseInstanceValues: true)]
63  public string Expression
64  {
65  get { return expression; }
66  set
67  {
68  if (expression == value) { return; }
69  expression = value;
70  previousReceivedSignal = "";
71  try
72  {
73  regex = new Regex(
74  @expression,
75  options: RegexOptions.None,
76  matchTimeout: timeout);
77  }
78  catch
79  {
80  return;
81  }
82  //reactivate the component, in case some faulty/malicious expression caused it to time out and deactivate itself
83  IsActive = true;
84  }
85  }
86 
88  : base(item, element)
89  {
90  nonContinuousOutputSent = true;
91  IsActive = true;
92  }
93 
94  public override void Update(float deltaTime, Camera cam)
95  {
96  if (string.IsNullOrWhiteSpace(expression) || regex == null) { return; }
97  if (!ContinuousOutput && nonContinuousOutputSent) { return; }
98 
99  if (receivedSignal != previousReceivedSignal && receivedSignal != null)
100  {
101  try
102  {
103  Match match = regex.Match(receivedSignal);
104  previousResult = match.Success;
105  previousGroups = UseCaptureGroup && previousResult ? match.Groups : null;
106  previousReceivedSignal = receivedSignal;
107  }
108  catch (Exception e)
109  {
110  if (e is RegexMatchTimeoutException)
111  {
112  item.SendSignal("TIMEOUT", "signal_out");
113  //deactivate the component if the expression caused it to time out
114  IsActive = false;
115  }
116  else
117  {
118  item.SendSignal("ERROR", "signal_out");
119  }
120  previousResult = false;
121  return;
122  }
123  }
124 
125  string signalOut;
126  bool allowEmptyStringOutput = false;
127  if (previousResult)
128  {
129  if (UseCaptureGroup)
130  {
131  if (previousGroups != null && previousGroups.TryGetValue(Output, out Group group))
132  {
133  signalOut = group.Value;
134  allowEmptyStringOutput = OutputEmptyCaptureGroup;
135  }
136  else
137  {
138  signalOut = FalseOutput;
139  }
140  }
141  else
142  {
143  signalOut = Output;
144  }
145  }
146  else
147  {
148  signalOut = FalseOutput;
149  }
150 
151  if (!string.IsNullOrEmpty(signalOut) || (allowEmptyStringOutput && signalOut == string.Empty)) { item.SendSignal(signalOut, "signal_out"); }
152  if (!ContinuousOutput)
153  {
154  nonContinuousOutputSent = true;
155  }
156  }
157 
158  public override void ReceiveSignal(Signal signal, Connection connection)
159  {
160  switch (connection.Name)
161  {
162  case "signal_in":
163  receivedSignal = signal.value;
164  nonContinuousOutputSent = false;
165  break;
166  case "set_output":
167  Output = signal.value;
168  break;
169  }
170  }
171  }
172 }
Submarine Submarine
Definition: Entity.cs:53
void SendSignal(string signal, string connectionName)
The base class for components holding the different functionalities of the item
RegExFindComponent(Item item, ContentXElement element)
override void ReceiveSignal(Signal signal, Connection connection)
override void Update(float deltaTime, Camera cam)