Client LuaCsForBarotrauma
ServerMsgLString.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Linq;
6 using System.Text.RegularExpressions;
7 
8 namespace Barotrauma
9 {
11  {
12  private static readonly Regex reFormattedMessage =
13  new Regex(@"^(?<variable>[\[\].a-z0-9_]+?)=(?<formatter>[a-z0-9_]+?)\‍((?<value>.+?)\‍)",
14  RegexOptions.Compiled | RegexOptions.IgnoreCase);
15 
16  private static readonly Regex reReplacedMessage = new Regex(@"^(?<variable>[\[\].a-z0-9_]+?)=(?<message>.*)$",
17  RegexOptions.Compiled | RegexOptions.IgnoreCase);
18 
19  private static readonly ImmutableDictionary<Identifier, Func<string, string?>> messageFormatters =
20  new Dictionary<Identifier, Func<string, string?>>
21  {
22  {
23  "duration".ToIdentifier(),
24  secondsValue => double.TryParse(secondsValue, out var seconds)
25  ? $"{TimeSpan.FromSeconds(seconds):g}"
26  : null
27  }
28  }.ToImmutableDictionary();
29 
30  private static readonly ImmutableHashSet<char> serverMessageCharacters =
31  new [] {'~', '[', ']', '='}.ToImmutableHashSet();
32 
33  private readonly string serverMessage;
34  private readonly ImmutableArray<string> messageSplit;
35 
36  public ServerMsgLString(string serverMsg)
37  {
38  serverMessage = serverMsg;
39  messageSplit = serverMessage.Split("/").ToImmutableArray();
40  }
41 
42  private static bool IsServerMessageWithVariables(string message) =>
43  serverMessageCharacters.All(message.Contains);
44 
45  private LoadedSuccessfully loadedSuccessfully = LoadedSuccessfully.Unknown;
46  public override bool Loaded
47  {
48  get
49  {
50  if (loadedSuccessfully == LoadedSuccessfully.Unknown) { RetrieveValue(); }
51  return loadedSuccessfully == LoadedSuccessfully.Yes;
52  }
53  }
54 
55  public override void RetrieveValue()
56  {
57  Dictionary<string, string> replacedMessages = new Dictionary<string, string>();
58  bool translationsFound = false;
59 
60  string? TranslateMessage(string input)
61  {
62  string? message = input;
63  if (message.EndsWith("~", StringComparison.Ordinal))
64  {
65  message = message.Substring(0, message.Length - 1);
66  }
67 
68  if (!IsServerMessageWithVariables(message) && !message.Contains('=')) // No variables, try to translate
69  {
70  foreach (var replacedMessage in replacedMessages)
71  {
72  message = message.Replace(replacedMessage.Key, replacedMessage.Value);
73  }
74 
75  if (message.Contains(" "))
76  {
77  return message;
78  } // Spaces found, do not translate
79 
80  var msg = TextManager.Get(message);
81  if (msg.Loaded) // If a translation was found, otherwise use the original
82  {
83  message = msg.Value;
84  translationsFound = true;
85  }
86  }
87  else
88  {
89  string? messageVariable = null;
90  var matchFormatted = reFormattedMessage.Match(message);
91  if (matchFormatted.Success)
92  {
93  var formatter = matchFormatted.Groups["formatter"].ToString().ToIdentifier();
94  if (messageFormatters.TryGetValue(formatter, out var formatterFn))
95  {
96  var formattedValue = formatterFn(matchFormatted.Groups["value"].ToString());
97  if (formattedValue != null)
98  {
99  messageVariable = matchFormatted.Groups["variable"].ToString();
100  message = formattedValue;
101  }
102  }
103  }
104 
105  if (messageVariable == null)
106  {
107  var matchReplaced = reReplacedMessage.Match(message);
108  if (matchReplaced.Success)
109  {
110  messageVariable = matchReplaced.Groups["variable"].ToString();
111  message = matchReplaced.Groups["message"].ToString();
112  }
113  }
114 
115  foreach (var replacedMessage in replacedMessages)
116  {
117  message = message.Replace(replacedMessage.Key, replacedMessage.Value);
118  }
119 
120  string[] messageWithVariables = message.Split('~');
121 
122  var msg = TextManager.Get(messageWithVariables[0]);
123 
124  if (msg.Loaded) // If a translation was found, otherwise use the original
125  {
126  message = msg.Value;
127  translationsFound = true;
128  }
129  else if (messageVariable == null)
130  {
131  return message; // No translation found, probably caused by player input -> skip variable handling
132  }
133 
134  // First index is always the message identifier -> start at 1
135  for (int j = 1; j < messageWithVariables.Length; j++)
136  {
137  string[] variableAndValue = messageWithVariables[j].Split('=');
138  message = message.Replace(variableAndValue[0],
139  variableAndValue[1].Length > 1 && variableAndValue[1][0] == 'ยง'
140  ? TextManager.Get(variableAndValue[1].Substring(1)).Value
141  : variableAndValue[1]);
142  }
143 
144  if (messageVariable != null)
145  {
146  replacedMessages[messageVariable] = message;
147  message = null;
148  }
149  }
150 
151  return message;
152  }
153 
154  try
155  {
156  string translatedServerMessage = "";
157 
158  for (int i = 0; i < messageSplit.Length; i++)
159  {
160  string? message = TranslateMessage(messageSplit[i]);
161  if (message != null)
162  {
163  translatedServerMessage += message;
164  }
165  }
166 
167  cachedValue = translationsFound ? translatedServerMessage : serverMessage;
168  loadedSuccessfully = LoadedSuccessfully.Yes;
169  }
170  catch (IndexOutOfRangeException exception)
171  {
172  string errorMsg = $"Failed to translate server message \"{serverMessage}\".";
173 #if DEBUG
174  DebugConsole.ThrowError(errorMsg, exception);
175 #endif
176  GameAnalyticsManager.AddErrorEventOnce($"TextManager.GetServerMessage:{serverMessage}",
177  GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
178  cachedValue = errorMsg;
179  loadedSuccessfully = LoadedSuccessfully.No;
180  }
181 
182  UpdateLanguage();
183  }
184  }
185 }
ServerMsgLString(string serverMsg)