Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -0,0 +1,35 @@
#nullable enable
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
{
public class AddedPunctuationLString : LocalizedString
{
private readonly ImmutableArray<LocalizedString> nestedStrs;
private readonly char punctuationSymbol;
public AddedPunctuationLString(char symbol, params LocalizedString[] nStrs) { nestedStrs = nStrs.ToImmutableArray(); punctuationSymbol = symbol; }
public override bool Loaded => nestedStrs.All(s => s.Loaded);
public override void RetrieveValue()
{
string separator = "";
if (GameSettings.CurrentConfig.Language == "French".ToLanguageIdentifier())
{
bool addNonBreakingSpace =
punctuationSymbol == ':' || punctuationSymbol == ';' ||
punctuationSymbol == '!' || punctuationSymbol == '?';
separator = addNonBreakingSpace ?
new string(new char[] { (char)(0xA0), punctuationSymbol, ' ' }) :
new string(new char[] { punctuationSymbol, ' ' });
}
else
{
separator = new string(new char[] { punctuationSymbol, ' ' });
}
cachedValue = string.Join(separator, nestedStrs.Select(str => str.Value));
UpdateLanguage();
}
}
}
@@ -0,0 +1,25 @@
#nullable enable
namespace Barotrauma
{
public class CapitalizeLString : LocalizedString
{
private readonly LocalizedString nestedStr;
public CapitalizeLString(LocalizedString nStr) { nestedStr = nStr; }
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
string str = nestedStr.Value;
if (!string.IsNullOrEmpty(str))
{
cachedValue = char.ToUpper(str[0]) + str[1..];
}
else
{
cachedValue = "";
}
UpdateLanguage();
}
}
}
@@ -0,0 +1,21 @@
#nullable enable
namespace Barotrauma
{
public class ConcatLString : LocalizedString
{
private readonly LocalizedString left;
private readonly LocalizedString right;
public ConcatLString(LocalizedString l, LocalizedString r)
{
left = l; right = r;
}
public override bool Loaded => left.Loaded || right.Loaded;
public override void RetrieveValue()
{
cachedValue = left.Value + right.Value;
UpdateLanguage();
}
}
}
@@ -0,0 +1,44 @@
#nullable enable
namespace Barotrauma
{
public class FallbackLString : LocalizedString
{
private readonly LocalizedString primary;
private readonly LocalizedString fallback;
private bool primaryIsLoaded = false;
public FallbackLString(LocalizedString primary, LocalizedString fallback)
{
if (primary is FallbackLString {primary: { } innerPrimary, fallback: { } innerFallback})
{
this.primary = innerPrimary;
this.fallback = innerFallback.Fallback(fallback);
}
else
{
this.primary = primary;
this.fallback = fallback;
}
}
protected override bool MustRetrieveValue()
{
return base.MustRetrieveValue()
|| MustRetrieveValue(primary)
|| MustRetrieveValue(fallback)
|| primaryIsLoaded != primary.Loaded;
}
public override bool Loaded => primary.Loaded || fallback.Loaded;
public override void RetrieveValue()
{
cachedValue = primary.Value;
primaryIsLoaded = primary.Loaded;
if (!primary.Loaded)
{
cachedValue = fallback.Value;
}
}
}
}
@@ -0,0 +1,25 @@
#nullable enable
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
{
public class FormattedLString : LocalizedString
{
private readonly LocalizedString str;
private readonly ImmutableArray<LocalizedString> subStrs;
public FormattedLString(LocalizedString str, params LocalizedString[] subStrs)
{
this.str = str;
this.subStrs = subStrs.ToImmutableArray();
}
public override bool Loaded => str.Loaded && subStrs.All(s => s.Loaded);
public override void RetrieveValue()
{
//TODO: possibly broken!
cachedValue = string.Format(str.Value, subStrs.Select(s => s.Value as object).ToArray());
UpdateLanguage();
}
}
}
@@ -0,0 +1,33 @@
#nullable enable
using System;
namespace Barotrauma
{
public class InputTypeLString : LocalizedString
{
private readonly LocalizedString nestedStr;
public InputTypeLString(LocalizedString nStr) { nestedStr = nStr; }
protected override bool MustRetrieveValue()
{
//TODO: check for config changes!
return base.MustRetrieveValue();
}
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
cachedValue = nestedStr.Value;
#if CLIENT
//TODO: server shouldn't have this type at all
foreach (InputType? inputType in Enum.GetValues(typeof(InputType)))
{
if (!inputType.HasValue) { continue; }
cachedValue = cachedValue.Replace($"[{inputType}]", GameSettings.CurrentConfig.KeyMap.KeyBindText(inputType.Value).Value, StringComparison.OrdinalIgnoreCase);
cachedValue = cachedValue.Replace($"[InputType.{inputType}]", GameSettings.CurrentConfig.KeyMap.KeyBindText(inputType.Value).Value, StringComparison.OrdinalIgnoreCase);
}
#endif
UpdateLanguage();
}
}
}
@@ -0,0 +1,24 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
public class JoinLString : LocalizedString
{
private readonly IEnumerable<LocalizedString> subStrs;
private readonly string separator;
public JoinLString(string separator, IEnumerable<LocalizedString> subStrs)
{
this.separator = separator; this.subStrs = subStrs;
}
public override bool Loaded => subStrs.All(s => s.Loaded);
public override void RetrieveValue()
{
cachedValue = string.Join(separator, subStrs);
UpdateLanguage();
}
}
}
@@ -0,0 +1,177 @@
#nullable enable
using System;
using System.Collections.Generic;
namespace Barotrauma
{
public abstract class LocalizedString : IComparable
{
protected enum LoadedSuccessfully
{
Unknown,
No,
Yes
}
protected LanguageIdentifier language { get; private set; } = LanguageIdentifier.None;
private int languageVersion = 0;
protected string cachedValue = "";
public string Value
{
get
{
if (MustRetrieveValue()) { RetrieveValue(); }
return cachedValue;
}
}
public int Length => Value.Length;
public abstract bool Loaded { get; }
protected void UpdateLanguage()
{
language = GameSettings.CurrentConfig.Language;
languageVersion = TextManager.LanguageVersion;
}
protected virtual bool MustRetrieveValue() //this can't be called on other LocalizedStrings by derived classes
{
return language != GameSettings.CurrentConfig.Language || languageVersion != TextManager.LanguageVersion;
}
protected static bool MustRetrieveValue(LocalizedString str) //this can be called by derived classes
{
return str.MustRetrieveValue();
}
public abstract void RetrieveValue();
public static implicit operator LocalizedString(string value) => new RawLString(value);
public static implicit operator LocalizedString(char value) => new RawLString(value.ToString());
public static LocalizedString operator+(LocalizedString left, LocalizedString right) => new ConcatLString(left, right);
public static LocalizedString operator+(LocalizedString left, object right) => left + (right.ToString() ?? "");
public static LocalizedString operator+(object left, LocalizedString right) => (left.ToString() ?? "") + right;
public static bool operator==(LocalizedString? left, LocalizedString? right)
{
return left?.Value == right?.Value;
}
public static bool operator!=(LocalizedString? left, LocalizedString? right)
{
return !(left == right);
}
public override string ToString()
{
return Value;
}
public bool Contains(string subStr, StringComparison comparison = StringComparison.Ordinal)
{
return !Value.IsNullOrEmpty() && Value.Contains(subStr, comparison);
}
public bool Contains(char chr, StringComparison comparison = StringComparison.Ordinal)
{
return Value.Contains(chr, comparison);
}
public virtual LocalizedString ToUpper()
{
return new UpperLString(this);
}
public static LocalizedString Join(string separator, params LocalizedString[] subStrs)
{
return Join(separator, (IEnumerable<LocalizedString>)subStrs);
}
public static LocalizedString Join(string separator, IEnumerable<LocalizedString> subStrs)
{
return new JoinLString(separator, subStrs);
}
public LocalizedString Fallback(LocalizedString fallback)
{
return new FallbackLString(this, fallback);
}
public IReadOnlyList<LocalizedString> Split(params char[] separators)
{
var splitter = new LStringSplitter(this, separators);
return splitter.Substrings;
}
public LocalizedString Replace(Identifier find, LocalizedString replace, StringComparison stringComparison = StringComparison.Ordinal)
{
return new ReplaceLString(this, stringComparison, (find, replace));
}
public LocalizedString Replace(string find, LocalizedString replace, StringComparison stringComparison = StringComparison.Ordinal)
{
return new ReplaceLString(this, stringComparison, (find.ToIdentifier(), replace));
}
public LocalizedString Replace(LocalizedString find, LocalizedString replace,
StringComparison stringComparison = StringComparison.Ordinal)
{
return new ReplaceLString(this, stringComparison, (find, replace));
}
public LocalizedString TrimStart()
{
return new TrimLString(this, TrimLString.Mode.Start);
}
public LocalizedString TrimEnd()
{
return new TrimLString(this, TrimLString.Mode.End);
}
public LocalizedString ToLower()
{
return new LowerLString(this);
}
public override bool Equals(object? obj)
{
if (obj is LocalizedString lStr) { return Equals(lStr, StringComparison.Ordinal); }
if (obj is string str) { return Equals(str, StringComparison.Ordinal); }
return base.Equals(obj);
}
public bool Equals(LocalizedString other, StringComparison comparison = StringComparison.Ordinal)
{
return Equals(other.Value, comparison);
}
public bool Equals(string other, StringComparison comparison = StringComparison.Ordinal)
{
return string.Equals(Value, other, comparison);
}
public bool StartsWith(LocalizedString other, StringComparison comparison = StringComparison.Ordinal)
{
return StartsWith(other.Value, comparison);
}
public bool StartsWith(string other, StringComparison comparison = StringComparison.Ordinal)
{
return Value.StartsWith(other, comparison);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public int CompareTo(object? obj)
{
return Value.CompareTo(obj?.ToString() ?? "");
}
}
}
@@ -0,0 +1,20 @@
#nullable enable
namespace Barotrauma
{
public class LowerLString : LocalizedString
{
private readonly LocalizedString nestedStr;
public LowerLString(LocalizedString nestedStr)
{
this.nestedStr = nestedStr;
}
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
cachedValue = nestedStr.Value.ToLowerInvariant();
UpdateLanguage();
}
}
}
@@ -0,0 +1,13 @@
#nullable enable
namespace Barotrauma
{
public class RawLString : LocalizedString
{
public RawLString(string value) { cachedValue = value; }
protected override bool MustRetrieveValue() => false;
public override bool Loaded => true;
public override void RetrieveValue() { }
}
}
@@ -0,0 +1,75 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
public class ReplaceLString : LocalizedString
{
private readonly LocalizedString nestedStr;
private readonly ImmutableDictionary<LocalizedString, (LocalizedString Value, FormatCapitals FormatCapitals)> replacements;
private readonly StringComparison stringComparison;
public ReplaceLString(LocalizedString nStr, StringComparison sc, IEnumerable<(LocalizedString Key, LocalizedString Value, FormatCapitals FormatCapitals)> r)
{
nestedStr = nStr;
replacements = r.Select(kvf => (kvf.Key, (kvf.Value, kvf.FormatCapitals))).ToImmutableDictionary();
stringComparison = sc;
}
public ReplaceLString(LocalizedString nStr, StringComparison sc, params (LocalizedString Key, LocalizedString Value)[] r)
: this(nStr, sc, r.Select(kv => (kv.Key, kv.Value, FormatCapitals.No))) { }
public ReplaceLString(LocalizedString nStr, StringComparison sc, IEnumerable<(Identifier Key, LocalizedString Value, FormatCapitals FormatCapitals)> r)
: this(nStr, sc, r.Select(p => ((LocalizedString)p.Key.Value, p.Value, p.FormatCapitals))) { }
public ReplaceLString(LocalizedString nStr, StringComparison sc, params (Identifier Key, LocalizedString Value)[] r)
: this(nStr, sc, r.Select(kv => ((LocalizedString)kv.Key.Value, kv.Value, FormatCapitals.No))) { }
private static string HandleVariableCapitalization(string text, string variableTag, string variableValue)
{
int index = text.IndexOf(variableTag, StringComparison.InvariantCulture) - 1;
if (index == -1)
{
return variableValue;
}
for (int i = index; i >= 0; i--)
{
if (char.IsWhiteSpace(text[i])) { continue; }
if (text[i] != '.')
{
variableValue = variableValue.ToLowerInvariant();
}
else
{
variableValue = TextManager.Capitalize(variableValue).Value;
break;
}
}
return variableValue;
}
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
cachedValue = nestedStr.Value;
foreach (var varName in replacements.Keys)
{
string key = varName.Value;
string value = replacements[varName].Value.Value;
if (replacements[varName].FormatCapitals == FormatCapitals.Yes)
{
value = HandleVariableCapitalization(cachedValue, key, value);
}
cachedValue = cachedValue.Replace(key, value, stringComparison);
}
UpdateLanguage();
}
}
}
@@ -0,0 +1,185 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.RegularExpressions;
namespace Barotrauma
{
public class ServerMsgLString : LocalizedString
{
private static readonly Regex reFormattedMessage =
new Regex(@"^(?<variable>[\[\].a-z0-9_]+?)=(?<formatter>[a-z0-9_]+?)\((?<value>.+?)\)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex reReplacedMessage = new Regex(@"^(?<variable>[\[\].a-z0-9_]+?)=(?<message>.*)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly ImmutableDictionary<Identifier, Func<string, string?>> messageFormatters =
new Dictionary<Identifier, Func<string, string?>>
{
{
"duration".ToIdentifier(),
secondsValue => double.TryParse(secondsValue, out var seconds)
? $"{TimeSpan.FromSeconds(seconds):g}"
: null
}
}.ToImmutableDictionary();
private static readonly ImmutableHashSet<char> serverMessageCharacters =
new [] {'~', '[', ']', '='}.ToImmutableHashSet();
private readonly string serverMessage;
private readonly ImmutableArray<string> messageSplit;
public ServerMsgLString(string serverMsg)
{
serverMessage = serverMsg;
messageSplit = serverMessage.Split("/").ToImmutableArray();
}
private static bool IsServerMessageWithVariables(string message) =>
serverMessageCharacters.All(message.Contains);
private LoadedSuccessfully loadedSuccessfully = LoadedSuccessfully.Unknown;
public override bool Loaded
{
get
{
if (loadedSuccessfully == LoadedSuccessfully.Unknown) { RetrieveValue(); }
return loadedSuccessfully == LoadedSuccessfully.Yes;
}
}
public override void RetrieveValue()
{
Dictionary<string, string> replacedMessages = new Dictionary<string, string>();
bool translationsFound = false;
string? TranslateMessage(string input)
{
string? message = input;
if (message.EndsWith("~", StringComparison.Ordinal))
{
message = message.Substring(0, message.Length - 1);
}
if (!IsServerMessageWithVariables(message) && !message.Contains('=')) // No variables, try to translate
{
foreach (var replacedMessage in replacedMessages)
{
message = message.Replace(replacedMessage.Key, replacedMessage.Value);
}
if (message.Contains(" "))
{
return message;
} // Spaces found, do not translate
var msg = TextManager.Get(message);
if (msg.Loaded) // If a translation was found, otherwise use the original
{
message = msg.Value;
translationsFound = true;
}
}
else
{
string? messageVariable = null;
var matchFormatted = reFormattedMessage.Match(message);
if (matchFormatted.Success)
{
var formatter = matchFormatted.Groups["formatter"].ToString().ToIdentifier();
if (messageFormatters.TryGetValue(formatter, out var formatterFn))
{
var formattedValue = formatterFn(matchFormatted.Groups["value"].ToString());
if (formattedValue != null)
{
messageVariable = matchFormatted.Groups["variable"].ToString();
message = formattedValue;
}
}
}
if (messageVariable == null)
{
var matchReplaced = reReplacedMessage.Match(message);
if (matchReplaced.Success)
{
messageVariable = matchReplaced.Groups["variable"].ToString();
message = matchReplaced.Groups["message"].ToString();
}
}
foreach (var replacedMessage in replacedMessages)
{
message = message.Replace(replacedMessage.Key, replacedMessage.Value);
}
string[] messageWithVariables = message.Split('~');
var msg = TextManager.Get(messageWithVariables[0]);
if (msg.Loaded) // If a translation was found, otherwise use the original
{
message = msg.Value;
translationsFound = true;
}
else if (messageVariable == null)
{
return message; // No translation found, probably caused by player input -> skip variable handling
}
// First index is always the message identifier -> start at 1
for (int j = 1; j < messageWithVariables.Length; j++)
{
string[] variableAndValue = messageWithVariables[j].Split('=');
message = message.Replace(variableAndValue[0],
variableAndValue[1].Length > 1 && variableAndValue[1][0] == '§'
? TextManager.Get(variableAndValue[1].Substring(1)).Value
: variableAndValue[1]);
}
if (messageVariable != null)
{
replacedMessages[messageVariable] = message;
message = null;
}
}
return message;
}
try
{
string translatedServerMessage = "";
for (int i = 0; i < messageSplit.Length; i++)
{
string? message = TranslateMessage(messageSplit[i]);
if (message != null)
{
translatedServerMessage += message;
}
}
cachedValue = translationsFound ? translatedServerMessage : serverMessage;
loadedSuccessfully = LoadedSuccessfully.Yes;
}
catch (IndexOutOfRangeException exception)
{
string errorMsg = $"Failed to translate server message \"{serverMessage}\".";
#if DEBUG
DebugConsole.ThrowError(errorMsg, exception);
#endif
GameAnalyticsManager.AddErrorEventOnce($"TextManager.GetServerMessage:{serverMessage}",
GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
cachedValue = errorMsg;
loadedSuccessfully = LoadedSuccessfully.No;
}
UpdateLanguage();
}
}
}
@@ -0,0 +1,93 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#nullable enable
namespace Barotrauma
{
public class LStringSplitter
{
public IReadOnlyList<LocalizedString> Substrings => substrings;
private class SubstringList : IReadOnlyList<LocalizedString>
{
public SubstringList(LStringSplitter splitter) { this.splitter = splitter; }
private LStringSplitter splitter;
private readonly List<LocalizedString> underlyingList = new List<LocalizedString>();
public List<LocalizedString> UnderlyingList
{
get
{
splitter.UpdateSubstrings();
return underlyingList;
}
}
public IEnumerator<LocalizedString> GetEnumerator() => UnderlyingList.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count => UnderlyingList.Count;
public LocalizedString this[int index] => UnderlyingList[index];
}
private readonly SubstringList substrings;
private readonly char[] separators;
private readonly LocalizedString originalString;
private string[] substrValues;
private string cachedOriginal;
public bool Loaded => originalString.Loaded;
public LStringSplitter(LocalizedString input, params char[] separators)
{
originalString = input;
substrings = new SubstringList(this);
substrValues = Array.Empty<string>();
this.separators = separators;
cachedOriginal = "";
}
private void UpdateSubstrings()
{
if (originalString.Value != cachedOriginal)
{
cachedOriginal = originalString.Value;
substrValues = cachedOriginal.Split(separators);
substrings.UnderlyingList.Clear();
substrings.UnderlyingList.AddRange(Enumerable.Range(0, substrValues.Length).Select(i => new SplitLString(this, i) as LocalizedString));
}
}
public string GetValue(int index)
{
UpdateSubstrings();
return substrValues[index];
}
}
public class SplitLString : LocalizedString
{
private bool loaded = false;
private readonly LStringSplitter splitter;
private readonly int index;
public SplitLString(LStringSplitter splitter, int index)
{
this.splitter = splitter; this.index = index;
}
public override bool Loaded => loaded && splitter.Loaded;
public override void RetrieveValue()
{
loaded = true;
cachedValue = splitter.GetValue(index);
UpdateLanguage();
}
}
}
@@ -0,0 +1,71 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.Extensions;
namespace Barotrauma
{
public class TagLString : LocalizedString
{
private readonly ImmutableArray<Identifier> tags;
public TagLString(params Identifier[] tags)
{
this.tags = tags.ToImmutableArray();
}
private LoadedSuccessfully loadedSuccessfully = LoadedSuccessfully.Unknown;
public override bool Loaded
{
get
{
if (loadedSuccessfully == LoadedSuccessfully.Unknown) { RetrieveValue(); }
return loadedSuccessfully == LoadedSuccessfully.Yes;
}
}
public override void RetrieveValue()
{
UpdateLanguage();
(string value, bool loaded) tryLoad(LanguageIdentifier lang)
{
IReadOnlyList<string> candidates = Array.Empty<string>();
int tagIndex = 0;
if (TextManager.TextPacks.TryGetValue(lang, out var packs))
{
while (candidates.Count == 0 && tagIndex < tags.Length)
{
foreach (var pack in packs)
{
if (pack.Texts.TryGetValue(tags[tagIndex], out var texts))
{
candidates = candidates.ListConcat(texts);
}
}
tagIndex++;
}
}
bool loaded = candidates.Count > 0;
return (loaded ? candidates.GetRandomUnsynced() : "", loaded);
}
var (value, loaded) = tryLoad(language);
loadedSuccessfully = loaded ? LoadedSuccessfully.Yes : LoadedSuccessfully.No;
cachedValue = value;
if (!loaded && language != TextManager.DefaultLanguage)
{
(value, _) = tryLoad(TextManager.DefaultLanguage);
cachedValue = value;
//Notice how we don't set loadedSuccessfully again here.
//This is by design; falling back to English means that
//this text did NOT load successfully, so Loaded must
//return false.
}
}
}
}
@@ -0,0 +1,28 @@
#nullable enable
using System;
namespace Barotrauma
{
public class TrimLString : LocalizedString
{
[Flags]
public enum Mode { Start = 0x1, End = 0x2, Both=0x3 }
private readonly LocalizedString nestedStr;
private readonly Mode mode;
public TrimLString(LocalizedString nestedStr, Mode mode)
{
this.nestedStr = nestedStr;
this.mode = mode;
}
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
cachedValue = nestedStr.Value;
if (mode.HasFlag(Mode.Start)) { cachedValue = cachedValue.TrimStart(); }
if (mode.HasFlag(Mode.End)) { cachedValue = cachedValue.TrimEnd(); }
UpdateLanguage();
}
}
}
@@ -0,0 +1,25 @@
#nullable enable
namespace Barotrauma
{
public class UpperLString : LocalizedString
{
private readonly LocalizedString nestedStr;
public UpperLString(LocalizedString nestedStr)
{
this.nestedStr = nestedStr;
}
public override bool Loaded => nestedStr.Loaded;
public override void RetrieveValue()
{
cachedValue = nestedStr.Value.ToUpper();
UpdateLanguage();
}
public override LocalizedString ToUpper()
{
return this;
}
}
}