Unstable 0.17.0.0
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class CollectionConcat<T> : ICollection<T>
|
||||
{
|
||||
protected readonly IEnumerable<T> enumerableA;
|
||||
protected readonly IEnumerable<T> enumerableB;
|
||||
|
||||
public CollectionConcat(IEnumerable<T> a, IEnumerable<T> b)
|
||||
{
|
||||
enumerableA = a; enumerableB = b;
|
||||
}
|
||||
|
||||
public int Count => enumerableA.Count()+enumerableB.Count();
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public void Add(T item) => throw new InvalidOperationException();
|
||||
|
||||
public void Clear() => throw new InvalidOperationException();
|
||||
|
||||
public bool Remove(T item) => throw new InvalidOperationException();
|
||||
|
||||
public bool Contains(T item) => enumerableA.Contains(item) || enumerableB.Contains(item);
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
void performCopy(IEnumerable<T> enumerable)
|
||||
{
|
||||
if (enumerable is ICollection<T> collection)
|
||||
{
|
||||
collection.CopyTo(array, arrayIndex);
|
||||
arrayIndex += collection.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
array[arrayIndex] = item;
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
performCopy(enumerableA);
|
||||
performCopy(enumerableB);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
foreach (T item in enumerableA) { yield return item; }
|
||||
foreach (T item in enumerableB) { yield return item; }
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
public class ListConcat<T> : CollectionConcat<T>, IList<T>, IReadOnlyList<T>
|
||||
{
|
||||
public ListConcat(IEnumerable<T> a, IEnumerable<T> b) : base(a, b) { }
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
int aCount = 0;
|
||||
if (enumerableA is IList<T> listA)
|
||||
{
|
||||
int index = listA.IndexOf(item);
|
||||
if (index >= 0) { return index; }
|
||||
aCount = listA.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var a in enumerableA)
|
||||
{
|
||||
if (object.Equals(item, a)) { return aCount; }
|
||||
aCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (enumerableB is IList<T> listB)
|
||||
{
|
||||
int index = listB.IndexOf(item);
|
||||
if (index >= 0) { return index + aCount; }
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var b in enumerableB)
|
||||
{
|
||||
if (object.Equals(item, b)) { return aCount; }
|
||||
aCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
int aCount = enumerableA.Count();
|
||||
return index < aCount ? enumerableA.ElementAt(index) : enumerableB.ElementAt(index - aCount);
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
#if DEBUG && MODBREAKER
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary that's been deliberately designed to be a piece of
|
||||
/// shit. Meant to be used to stress-test scenarios where we might
|
||||
/// accidentally be relying on the implementation details of a
|
||||
/// dictionary that shouldn't be relied on.
|
||||
/// </summary>
|
||||
public class CursedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> where TKey: notnull
|
||||
{
|
||||
private ICollection<TKey> keys;
|
||||
private ICollection<TValue> values;
|
||||
private readonly List<KeyValuePair<TKey, TValue>> keyValuePairs = new List<KeyValuePair<TKey, TValue>>();
|
||||
private readonly Dictionary<TKey, int> keyToKvpIndex = new Dictionary<TKey, int>();
|
||||
private readonly object mutex = new object();
|
||||
|
||||
private readonly Random rng;
|
||||
|
||||
public CursedDictionary()
|
||||
{
|
||||
rng = new Random((int)(DateTime.Now.ToBinary() % int.MaxValue));
|
||||
keys = Array.Empty<TKey>();
|
||||
values = Array.Empty<TValue>();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
keys = keyValuePairs.Select(kvp => kvp.Key).ToArray();
|
||||
values = keyValuePairs.Select(kvp => kvp.Value).ToArray();
|
||||
keyToKvpIndex.Clear();
|
||||
for (int i=0; i<keyValuePairs.Count; i++)
|
||||
{
|
||||
keyToKvpIndex[keyValuePairs[i].Key] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
KeyValuePair<TKey, TValue>[] clone;
|
||||
lock (mutex)
|
||||
{
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
clone = keyValuePairs.ToArray();
|
||||
}
|
||||
|
||||
foreach (var kvp in clone)
|
||||
{
|
||||
yield return kvp;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
if (keyToKvpIndex.ContainsKey(item.Key))
|
||||
{
|
||||
throw new InvalidOperationException($"Duplicate key: {item.Key}");
|
||||
}
|
||||
|
||||
keyValuePairs.Add(item);
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
keyValuePairs.Clear();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return keyValuePairs.Contains(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
keyValuePairs.Shuffle(rng);
|
||||
keyValuePairs.CopyTo(array, arrayIndex);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
bool success = keyValuePairs.Remove(item);
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return keyValuePairs.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TKey key, TValue value) => Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return keyToKvpIndex.ContainsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
value = default!;
|
||||
bool success = keyToKvpIndex.TryGetValue(key, out int index);
|
||||
if (success)
|
||||
{
|
||||
value = keyValuePairs[index].Value;
|
||||
}
|
||||
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key) => TryRemove(key, out _);
|
||||
|
||||
public bool TryRemove(TKey key, out TValue value)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
value = default!;
|
||||
bool success = false;
|
||||
if (keyToKvpIndex.TryGetValue(key, out int index))
|
||||
{
|
||||
value = keyValuePairs[index].Value;
|
||||
keyValuePairs.RemoveAt(index);
|
||||
success = true;
|
||||
}
|
||||
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return keyValuePairs[keyToKvpIndex[key]].Value;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
if (!keyToKvpIndex.ContainsKey(key))
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyValuePairs[keyToKvpIndex[key]] = new KeyValuePair<TKey, TValue>(key, value);
|
||||
}
|
||||
|
||||
keyValuePairs.Shuffle(rng);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return keys.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
return values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -7,7 +7,7 @@ namespace Barotrauma
|
||||
class Homoglyphs
|
||||
{
|
||||
///List of homoglyphs taken from https://github.com/codebox/homoglyph/
|
||||
private static List<uint[]> homoglyphs = new List<uint[]>(){
|
||||
private readonly static uint[][] homoglyphs = {
|
||||
new uint[]{0x20,0xa0,0x1680,0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,0x2006,0x2007,0x2008,0x2009,0x200a,0x2028,0x2029,0x202f,0x205f},
|
||||
new uint[]{0x21,0x1c3,0x2d51,0xff01},
|
||||
new uint[]{0x24,0xff04},
|
||||
|
||||
@@ -35,9 +35,9 @@ namespace Barotrauma
|
||||
{
|
||||
public sealed class HttpUtility
|
||||
{
|
||||
public static Dictionary<string, string> ParseQueryString(string query)
|
||||
public static Dictionary<Identifier, string> ParseQueryString(string query)
|
||||
{
|
||||
Dictionary<string, string> collection = new Dictionary<string, string>();
|
||||
Dictionary<Identifier, string> collection = new Dictionary<Identifier, string>();
|
||||
var splitGet = query.Split('?');
|
||||
if (splitGet.Length > 1)
|
||||
{
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma
|
||||
var splitKeyValue = kvp.Split('=');
|
||||
if (splitKeyValue.Length > 1)
|
||||
{
|
||||
collection.Add(splitKeyValue[0], splitKeyValue[1]);
|
||||
collection.Add(splitKeyValue[0].ToIdentifier(), splitKeyValue[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ namespace Barotrauma
|
||||
if (parentElement is { HasElements: true })
|
||||
{
|
||||
srcRanges = new List<Range<int>>();
|
||||
foreach (XElement subElement in parentElement.Elements())
|
||||
foreach (var subElement in parentElement.Elements())
|
||||
{
|
||||
int id = subElement.GetAttributeInt("ID", -1);
|
||||
if (id > 0) { InsertId(id); }
|
||||
}
|
||||
maxId = GetOffsetId(srcRanges.Last().End);
|
||||
maxId = GetOffsetId(srcRanges.Any() ? srcRanges.Last().End : offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class LinkedPairSet<T1, T2> : IEnumerable<(T1, T2)>
|
||||
{
|
||||
private readonly Dictionary<T1, T2> t1ToT2 = new Dictionary<T1, T2>();
|
||||
private readonly Dictionary<T2, T1> t2ToT1 = new Dictionary<T2, T1>();
|
||||
|
||||
public bool Contains(T1 t1)
|
||||
{
|
||||
return t1ToT2.ContainsKey(t1);
|
||||
}
|
||||
|
||||
public bool Contains(T2 t2)
|
||||
{
|
||||
return t2ToT1.ContainsKey(t2);
|
||||
}
|
||||
|
||||
public T2 this[T1 t1]
|
||||
{
|
||||
get { return t1ToT2[t1]; }
|
||||
set
|
||||
{
|
||||
T2 prevT2 = t1ToT2[t1];
|
||||
t2ToT1.Remove(prevT2); t2ToT1.Add(value, t1);
|
||||
t1ToT2[t1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public T1 this[T2 t2]
|
||||
{
|
||||
get { return t2ToT1[t2]; }
|
||||
set
|
||||
{
|
||||
T1 prevT1 = t2ToT1[t2];
|
||||
t1ToT2.Remove(prevT1); t1ToT2.Add(value, t2);
|
||||
t2ToT1[t2] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(T1 t1, T2 t2)
|
||||
{
|
||||
if (Contains(t1)) { throw new ArgumentException($"{GetType().Name} already contains {t1}"); }
|
||||
if (Contains(t2)) { throw new ArgumentException($"{GetType().Name} already contains {t2}"); }
|
||||
t1ToT2.Add(t1, t2);
|
||||
t2ToT1.Add(t2, t1);
|
||||
}
|
||||
|
||||
public void Remove(T1 t1)
|
||||
{
|
||||
T2 t2 = t1ToT2[t1];
|
||||
t1ToT2.Remove(t1);
|
||||
t2ToT1.Remove(t2);
|
||||
}
|
||||
|
||||
public void Remove(T2 t2)
|
||||
{
|
||||
T1 t1 = t2ToT1[t2];
|
||||
t1ToT2.Remove(t1);
|
||||
t2ToT1.Remove(t2);
|
||||
}
|
||||
|
||||
public IEnumerator<(T1, T2)> GetEnumerator()
|
||||
{
|
||||
foreach (var t1 in t1ToT2.Keys)
|
||||
{
|
||||
yield return (t1, t1ToT2[t1]);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class ListDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly ImmutableDictionary<TKey, int> keyToIndex;
|
||||
private readonly IReadOnlyList<TValue> list;
|
||||
|
||||
public ListDictionary(IReadOnlyList<TValue> list, int len, Func<int, TKey> keyFunc)
|
||||
{
|
||||
this.list = list;
|
||||
var keyToIndex = new Dictionary<TKey, int>();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
keyToIndex.Add(keyFunc(i), i);
|
||||
}
|
||||
|
||||
this.keyToIndex = keyToIndex.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
foreach (var kvp in keyToIndex)
|
||||
{
|
||||
yield return new KeyValuePair<TKey, TValue>(kvp.Key, list[kvp.Value]);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public int Count => keyToIndex.Count;
|
||||
public bool ContainsKey(TKey key) => keyToIndex.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
if (keyToIndex.TryGetValue(key, out int index))
|
||||
{
|
||||
value = list[index];
|
||||
return true;
|
||||
}
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
public TValue this[TKey key] => list[keyToIndex[key]];
|
||||
|
||||
public IEnumerable<TKey> Keys => keyToIndex.Keys;
|
||||
public IEnumerable<TValue> Values => keyToIndex.Values.Select(i => list[i]);
|
||||
}
|
||||
}
|
||||
@@ -749,7 +749,7 @@ namespace Barotrauma
|
||||
|
||||
Vector2 normal = Vector2.Normalize(endSegment - startSegment);
|
||||
normal = new Vector2(-normal.Y, normal.X);
|
||||
midPoint += normal * Rand.Range(-offsetAmount, offsetAmount, Rand.RandSync.Server);
|
||||
midPoint += normal * Rand.Range(-offsetAmount, offsetAmount, Rand.RandSync.ServerAndClient);
|
||||
|
||||
if (bounds.HasValue)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
public sealed class None<T> : Option<T>
|
||||
{
|
||||
private None() { }
|
||||
|
||||
public static Option<T> Create() => new None<T>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <a href="https://en.wikipedia.org/wiki/Option_type">Option type</a>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Credit <a href="https://github.com/Jlobblet/FunctionalStuff/tree/main/src/FunctionalStuff/Option">Jlobblet</a>
|
||||
/// </remarks>
|
||||
public abstract class Option<T>
|
||||
{
|
||||
public static Option<T> Some(T value) => Some<T>.Create(value);
|
||||
public static Option<T> None() => None<T>.Create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public sealed class Some<T> : Option<T>
|
||||
{
|
||||
public readonly T Value;
|
||||
|
||||
private Some(T value)
|
||||
{
|
||||
if (value is null) { throw new ArgumentNullException(nameof(value), "Some<T> cannot contain null"); }
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static Option<T> Create(T value) => new Some<T>(value);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,68 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Barotrauma.IO;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class Rand
|
||||
{
|
||||
[Obsolete("TODO: remove")]
|
||||
public static class Tracker
|
||||
{
|
||||
private readonly static List<string> logMsgs = new List<string>();
|
||||
public static IReadOnlyList<string> LogMsgs => logMsgs;
|
||||
|
||||
public static bool Active = false;
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
logMsgs.Clear();
|
||||
Active = false;
|
||||
}
|
||||
|
||||
public static void RegisterCall(int stDepth=4)
|
||||
{
|
||||
if (!Active) { return; }
|
||||
var st = new StackTrace(skipFrames: 2, fNeedFileInfo: true);
|
||||
var frames = st.GetFrames();
|
||||
string msg = string.Join("; ",
|
||||
frames.Take(stDepth).Select(f =>
|
||||
$"{Path.GetFileNameWithoutExtension(f.GetFileName())}:{f.GetFileLineNumber()}"));
|
||||
logMsgs.Add(msg);
|
||||
}
|
||||
|
||||
public static void Log(string msg)
|
||||
{
|
||||
if (!Active) { return; }
|
||||
logMsgs.Add(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public enum RandSync
|
||||
{
|
||||
Unsynced = -1, //not synced, used for unimportant details like minor particle properties
|
||||
Server = 0, //synced with the server (used for gameplay elements that the players can interact with)
|
||||
ClientOnly = 1 //set to match between clients (used for misc elements that the server doesn't track, but clients want to match anyway)
|
||||
Unsynced, //not synced, used for unimportant details like minor particle properties
|
||||
ServerAndClient, //synced with the server (used for gameplay elements that the players can interact with)
|
||||
#if CLIENT
|
||||
ClientOnly //set to match between clients (used for misc elements that the server doesn't track, but clients want to match anyway)
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Random localRandom = new Random();
|
||||
private static readonly Random[] syncedRandom = new MTRandom[] {
|
||||
new MTRandom(), new MTRandom()
|
||||
private static readonly Dictionary<RandSync, Random> syncedRandom = new Dictionary<RandSync, Random> {
|
||||
{ RandSync.ServerAndClient, new MTRandom() },
|
||||
#if CLIENT
|
||||
{ RandSync.ClientOnly, new MTRandom() }
|
||||
#endif
|
||||
};
|
||||
|
||||
public static Random GetRNG(RandSync randSync)
|
||||
{
|
||||
return randSync == RandSync.Unsynced ? localRandom : syncedRandom[(int)randSync];
|
||||
return randSync == RandSync.Unsynced ? localRandom : syncedRandom[randSync];
|
||||
}
|
||||
|
||||
public static void SetLocalRandom(int seed)
|
||||
@@ -30,21 +72,32 @@ namespace Barotrauma
|
||||
|
||||
public static void SetSyncedSeed(int seed)
|
||||
{
|
||||
syncedRandom[(int)RandSync.Server] = new MTRandom(seed);
|
||||
syncedRandom[(int)RandSync.ClientOnly] = new MTRandom(seed);
|
||||
syncedRandom[RandSync.ServerAndClient] = new MTRandom(seed);
|
||||
#if CLIENT
|
||||
syncedRandom[RandSync.ClientOnly] = new MTRandom(seed);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static int ThreadId = 0;
|
||||
private static void CheckRandThreadSafety(RandSync sync)
|
||||
{
|
||||
if (ThreadId != 0 && sync == RandSync.Server)
|
||||
if (sync == RandSync.ServerAndClient) { Tracker.RegisterCall(); }
|
||||
|
||||
if (ThreadId != 0 && sync == RandSync.Unsynced)
|
||||
{
|
||||
if (System.Threading.Thread.CurrentThread.ManagedThreadId != ThreadId)
|
||||
{
|
||||
Debug.WriteLine($"Unsynced rand used in synced thread! {Environment.StackTrace}");
|
||||
}
|
||||
}
|
||||
if (ThreadId != 0 && sync == RandSync.ServerAndClient)
|
||||
{
|
||||
if (System.Threading.Thread.CurrentThread.ManagedThreadId != ThreadId)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception("Unauthorized multithreaded access to RandSync.Server");
|
||||
throw new Exception("Unauthorized multithreaded access to RandSync.ServerAndClient");
|
||||
#else
|
||||
DebugConsole.ThrowError("Unauthorized multithreaded access to RandSync.Server\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
DebugConsole.ThrowError("Unauthorized multithreaded access to RandSync.ServerAndClient\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -53,13 +106,13 @@ namespace Barotrauma
|
||||
public static float Range(float minimum, float maximum, RandSync sync=RandSync.Unsynced)
|
||||
{
|
||||
CheckRandThreadSafety(sync);
|
||||
return (float)(sync == RandSync.Unsynced ? localRandom : (syncedRandom[(int)sync])).NextDouble() * (maximum - minimum) + minimum;
|
||||
return (float)(sync == RandSync.Unsynced ? localRandom : (syncedRandom[sync])).NextDouble() * (maximum - minimum) + minimum;
|
||||
}
|
||||
|
||||
public static double Range(double minimum, double maximum, RandSync sync = RandSync.Unsynced)
|
||||
{
|
||||
CheckRandThreadSafety(sync);
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[(int)sync])).NextDouble() * (maximum - minimum) + minimum;
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[sync])).NextDouble() * (maximum - minimum) + minimum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,13 +121,13 @@ namespace Barotrauma
|
||||
public static int Range(int minimum, int maximum, RandSync sync = RandSync.Unsynced)
|
||||
{
|
||||
CheckRandThreadSafety(sync);
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[(int)sync])).Next(maximum - minimum) + minimum;
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[sync])).Next(maximum - minimum) + minimum;
|
||||
}
|
||||
|
||||
public static int Int(int max, RandSync sync = RandSync.Unsynced)
|
||||
{
|
||||
CheckRandThreadSafety(sync);
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[(int)sync])).Next(max);
|
||||
return (sync == RandSync.Unsynced ? localRandom : (syncedRandom[sync])).Next(max);
|
||||
}
|
||||
|
||||
public static Vector2 Vector(float length, RandSync sync = RandSync.Unsynced)
|
||||
|
||||
@@ -7,10 +7,13 @@ namespace Barotrauma
|
||||
public static class ReadOnlyListExtensions
|
||||
{
|
||||
public static int IndexOf<T>(this IReadOnlyList<T> list, T elem)
|
||||
=> list.IndexOf(input => input.Equals(elem));
|
||||
|
||||
public static int IndexOf<T>(this IReadOnlyList<T> list, Func<T, bool> predicate)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (list[i].Equals(elem)) { return i; }
|
||||
if (predicate(list[i])) { return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class ReflectionUtils
|
||||
{
|
||||
public static IEnumerable<Type> GetDerivedNonAbstract<T>()
|
||||
{
|
||||
return Assembly.GetEntryAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#nullable enable
|
||||
namespace Barotrauma
|
||||
{
|
||||
public abstract class Result<T, TError>
|
||||
where T: notnull
|
||||
where TError: notnull
|
||||
{
|
||||
public abstract bool IsSuccess { get; }
|
||||
public bool IsFailure => !IsSuccess;
|
||||
|
||||
public static Success<T, TError> Success(T value)
|
||||
=> new Success<T, TError>(value);
|
||||
|
||||
public static Failure<T, TError> Failure(TError error)
|
||||
=> new Failure<T, TError>(error);
|
||||
}
|
||||
|
||||
public sealed class Success<T, TError> : Result<T, TError>
|
||||
where T: notnull
|
||||
where TError: notnull
|
||||
{
|
||||
public readonly T Value;
|
||||
public override bool IsSuccess => true;
|
||||
|
||||
public Success(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Failure<T, TError> : Result<T, TError>
|
||||
where T: notnull
|
||||
where TError: notnull
|
||||
{
|
||||
public readonly TError Error;
|
||||
public override bool IsSuccess => false;
|
||||
|
||||
public Failure(TError error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -20,18 +21,17 @@ namespace Barotrauma
|
||||
private const string metadataDefinition = "metadata";
|
||||
private const string endDefinition = "end";
|
||||
|
||||
public static List<RichTextData> GetRichTextData(string text, out string sanitizedText)
|
||||
public static ImmutableArray<RichTextData>? GetRichTextData(string text, out string sanitizedText)
|
||||
{
|
||||
List<RichTextData> textColors = null;
|
||||
sanitizedText = text;
|
||||
if (!string.IsNullOrEmpty(text) && text.Contains(definitionIndicator))
|
||||
if (!string.IsNullOrEmpty(text) && text.Contains(definitionIndicator, System.StringComparison.Ordinal))
|
||||
{
|
||||
text = text.Replace("\r", "");
|
||||
string[] segments = text.Split(definitionIndicator);
|
||||
|
||||
sanitizedText = string.Empty;
|
||||
|
||||
textColors = new List<RichTextData>();
|
||||
List<RichTextData> textColors = new List<RichTextData>();
|
||||
RichTextData tempData = null;
|
||||
|
||||
int prevIndex = 0;
|
||||
@@ -80,9 +80,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
return textColors.ToImmutableArray();
|
||||
}
|
||||
|
||||
return textColors;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -6,7 +8,7 @@ namespace Barotrauma.IO
|
||||
{
|
||||
static class Validation
|
||||
{
|
||||
private static readonly string[] unwritableDirs = new string[] { "Content", "Data/ContentPackages" };
|
||||
private static readonly string[] unwritableDirs = new string[] { "Content" };
|
||||
private static readonly string[] unwritableExtensions = new string[]
|
||||
{
|
||||
".pdb", ".com", ".scr", ".dylib", ".so", ".a", ".app", //executables and libraries (.exe and .dll handled separately in CanWrite)
|
||||
@@ -58,7 +60,11 @@ namespace Barotrauma.IO
|
||||
|
||||
public static class SafeXML
|
||||
{
|
||||
public static void SaveSafe(this System.Xml.Linq.XDocument doc, string path, bool throwExceptions = false)
|
||||
public static void SaveSafe(
|
||||
this System.Xml.Linq.XDocument doc,
|
||||
string path,
|
||||
System.Xml.Linq.SaveOptions saveOptions = System.Xml.Linq.SaveOptions.None,
|
||||
bool throwExceptions = false)
|
||||
{
|
||||
if (!Validation.CanWrite(path, false))
|
||||
{
|
||||
@@ -73,7 +79,7 @@ namespace Barotrauma.IO
|
||||
}
|
||||
return;
|
||||
}
|
||||
doc.Save(path);
|
||||
doc.Save(path, saveOptions);
|
||||
}
|
||||
|
||||
public static void SaveSafe(this System.Xml.Linq.XElement element, string path, bool throwExceptions = false)
|
||||
@@ -107,7 +113,7 @@ namespace Barotrauma.IO
|
||||
|
||||
public class XmlWriter : IDisposable
|
||||
{
|
||||
public readonly System.Xml.XmlWriter Writer;
|
||||
public readonly System.Xml.XmlWriter? Writer;
|
||||
|
||||
public XmlWriter(string path, System.Xml.XmlWriterSettings settings)
|
||||
{
|
||||
@@ -156,65 +162,42 @@ namespace Barotrauma.IO
|
||||
}
|
||||
}
|
||||
|
||||
public static class XmlWriterExtensions
|
||||
{
|
||||
public static void Save(this System.Xml.Linq.XDocument doc, XmlWriter writer)
|
||||
{
|
||||
doc.Save(writer.Writer ?? throw new NullReferenceException("Unable to save XML document: XML writer is null."));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Path
|
||||
{
|
||||
public static readonly char DirectorySeparatorChar = System.IO.Path.DirectorySeparatorChar;
|
||||
public static readonly char AltDirectorySeparatorChar = System.IO.Path.AltDirectorySeparatorChar;
|
||||
|
||||
public static string GetExtension(string path)
|
||||
{
|
||||
return System.IO.Path.GetExtension(path);
|
||||
}
|
||||
public static string GetExtension(string path) => System.IO.Path.GetExtension(path);
|
||||
|
||||
public static string GetFileNameWithoutExtension(string path)
|
||||
{
|
||||
return System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
public static string GetFileNameWithoutExtension(string path) => System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
public static string GetPathRoot(string path)
|
||||
{
|
||||
return System.IO.Path.GetPathRoot(path);
|
||||
}
|
||||
public static string? GetPathRoot(string? path) => System.IO.Path.GetPathRoot(path);
|
||||
|
||||
public static string GetRelativePath(string relativeTo, string path)
|
||||
{
|
||||
return System.IO.Path.GetRelativePath(relativeTo, path);
|
||||
}
|
||||
public static string GetRelativePath(string relativeTo, string path) => System.IO.Path.GetRelativePath(relativeTo, path);
|
||||
|
||||
public static string GetDirectoryName(string path)
|
||||
{
|
||||
return System.IO.Path.GetDirectoryName(path);
|
||||
}
|
||||
public static string GetDirectoryName(ContentPath path) => GetDirectoryName(path.Value)!;
|
||||
|
||||
public static string? GetDirectoryName(string path) => System.IO.Path.GetDirectoryName(path);
|
||||
|
||||
public static string GetFileName(string path)
|
||||
{
|
||||
return System.IO.Path.GetFileName(path);
|
||||
}
|
||||
public static string GetFileName(string path) => System.IO.Path.GetFileName(path);
|
||||
|
||||
public static string GetFullPath(string path)
|
||||
{
|
||||
return System.IO.Path.GetFullPath(path);
|
||||
}
|
||||
public static string GetFullPath(string path) => System.IO.Path.GetFullPath(path);
|
||||
|
||||
public static string Combine(params string[] s)
|
||||
{
|
||||
return System.IO.Path.Combine(s);
|
||||
}
|
||||
public static string Combine(params string[] s) => System.IO.Path.Combine(s);
|
||||
|
||||
public static string GetTempFileName()
|
||||
{
|
||||
return System.IO.Path.GetTempFileName();
|
||||
}
|
||||
public static string GetTempFileName() => System.IO.Path.GetTempFileName();
|
||||
|
||||
public static bool IsPathRooted(string path)
|
||||
{
|
||||
return System.IO.Path.IsPathRooted(path);
|
||||
}
|
||||
public static IEnumerable<char> GetInvalidFileNameChars()
|
||||
{
|
||||
return System.IO.Path.GetInvalidFileNameChars();
|
||||
}
|
||||
public static bool IsPathRooted(string path) => System.IO.Path.IsPathRooted(path);
|
||||
|
||||
public static IEnumerable<char> GetInvalidFileNameChars() => System.IO.Path.GetInvalidFileNameChars();
|
||||
}
|
||||
|
||||
public static class Directory
|
||||
@@ -229,22 +212,22 @@ namespace Barotrauma.IO
|
||||
System.IO.Directory.SetCurrentDirectory(path);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFiles(string path)
|
||||
public static string[] GetFiles(string path)
|
||||
{
|
||||
return System.IO.Directory.GetFiles(path);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFiles(string path, string pattern, System.IO.SearchOption option = System.IO.SearchOption.AllDirectories)
|
||||
public static string[] GetFiles(string path, string pattern, System.IO.SearchOption option = System.IO.SearchOption.AllDirectories)
|
||||
{
|
||||
return System.IO.Directory.GetFiles(path, pattern, option);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetDirectories(string path)
|
||||
public static string[] GetDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
return System.IO.Directory.GetDirectories(path);
|
||||
return System.IO.Directory.GetDirectories(path, searchPattern, searchOption);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFileSystemEntries(string path)
|
||||
public static string[] GetFileSystemEntries(string path)
|
||||
{
|
||||
return System.IO.Directory.GetFileSystemEntries(path);
|
||||
}
|
||||
@@ -264,7 +247,7 @@ namespace Barotrauma.IO
|
||||
return System.IO.Directory.Exists(path);
|
||||
}
|
||||
|
||||
public static System.IO.DirectoryInfo CreateDirectory(string path)
|
||||
public static System.IO.DirectoryInfo? CreateDirectory(string path)
|
||||
{
|
||||
if (!Validation.CanWrite(path, true))
|
||||
{
|
||||
@@ -289,10 +272,9 @@ namespace Barotrauma.IO
|
||||
|
||||
public static class File
|
||||
{
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
return System.IO.File.Exists(path);
|
||||
}
|
||||
public static bool Exists(ContentPath path) => Exists(path.Value);
|
||||
|
||||
public static bool Exists(string path) => System.IO.File.Exists(path);
|
||||
|
||||
public static void Copy(string src, string dest, bool overwrite=false)
|
||||
{
|
||||
@@ -319,6 +301,8 @@ namespace Barotrauma.IO
|
||||
System.IO.File.Move(src, dest);
|
||||
}
|
||||
|
||||
public static void Delete(ContentPath path) => Delete(path.Value);
|
||||
|
||||
public static void Delete(string path)
|
||||
{
|
||||
if (!Validation.CanWrite(path, false))
|
||||
@@ -334,7 +318,7 @@ namespace Barotrauma.IO
|
||||
return System.IO.File.GetLastWriteTime(path);
|
||||
}
|
||||
|
||||
public static FileStream Open(
|
||||
public static FileStream? Open(
|
||||
string path,
|
||||
System.IO.FileMode mode,
|
||||
System.IO.FileAccess access = System.IO.FileAccess.ReadWrite,
|
||||
@@ -362,17 +346,17 @@ namespace Barotrauma.IO
|
||||
return new FileStream(path, System.IO.File.Open(path, mode, access, shareVal));
|
||||
}
|
||||
|
||||
public static FileStream OpenRead(string path)
|
||||
public static FileStream? OpenRead(string path)
|
||||
{
|
||||
return Open(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
|
||||
}
|
||||
|
||||
public static FileStream OpenWrite(string path)
|
||||
public static FileStream? OpenWrite(string path)
|
||||
{
|
||||
return Open(path, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write);
|
||||
}
|
||||
|
||||
public static FileStream Create(string path)
|
||||
public static FileStream? Create(string path)
|
||||
{
|
||||
return Open(path, System.IO.FileMode.Create, System.IO.FileAccess.Write);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma
|
||||
if (ownedSubsElement != null)
|
||||
{
|
||||
ownedSubmarines = new List<SubmarineInfo>();
|
||||
foreach (XElement subElement in ownedSubsElement.Elements())
|
||||
foreach (var subElement in ownedSubsElement.Elements())
|
||||
{
|
||||
string subName = subElement.GetAttributeString("name", "");
|
||||
string ownedSubPath = Path.Combine(TempPath, subName + ".sub");
|
||||
@@ -271,7 +271,7 @@ namespace Barotrauma
|
||||
string folder = saveType == SaveType.Singleplayer ? SaveFolder : MultiplayerSaveFolder;
|
||||
if (fileName == "Save_Default")
|
||||
{
|
||||
fileName = TextManager.Get("SaveFile.DefaultName", true);
|
||||
fileName = TextManager.Get("SaveFile.DefaultName").Value;
|
||||
if (fileName.Length == 0) fileName = "Save";
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ namespace Barotrauma
|
||||
char c = BitConverter.ToChar(bytes, 0);
|
||||
sb.Append(c);
|
||||
}
|
||||
string sFileName = sb.ToString();
|
||||
string sFileName = sb.ToString().Replace('\\', '/');
|
||||
|
||||
fileName = sFileName;
|
||||
progress?.Invoke(sFileName);
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Barotrauma
|
||||
enumPath = string.IsNullOrWhiteSpace(startPath) ? "./" : startPath;
|
||||
}
|
||||
|
||||
List<string> filePaths = Directory.GetFileSystemEntries(enumPath).Select(Path.GetFileName).ToList();
|
||||
string[] filePaths = Directory.GetFileSystemEntries(enumPath).Select(Path.GetFileName).ToArray();
|
||||
|
||||
if (filePaths.Any(s => s.Equals(subDir, StringComparison.Ordinal)))
|
||||
{
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> correctedPaths = filePaths.Where(s => s.Equals(subDir, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
string[] correctedPaths = filePaths.Where(s => s.Equals(subDir, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
if (correctedPaths.Any())
|
||||
{
|
||||
corrected = true;
|
||||
@@ -159,7 +159,7 @@ namespace Barotrauma
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private static System.Text.RegularExpressions.Regex removeBBCodeRegex =
|
||||
private static readonly System.Text.RegularExpressions.Regex removeBBCodeRegex =
|
||||
new System.Text.RegularExpressions.Regex(@"\[\/?(?:b|i|u|url|quote|code|img|color|size)*?.*?\]");
|
||||
|
||||
public static string RemoveBBCodeTags(string str)
|
||||
@@ -168,15 +168,6 @@ namespace Barotrauma
|
||||
return removeBBCodeRegex.Replace(str, "");
|
||||
}
|
||||
|
||||
public static string LimitString(string str, int maxCharacters)
|
||||
{
|
||||
if (str == null || maxCharacters < 0) return null;
|
||||
|
||||
if (maxCharacters < 4 || str.Length <= maxCharacters) return str;
|
||||
|
||||
return str.Substring(0, maxCharacters - 3) + "...";
|
||||
}
|
||||
|
||||
public static string RandomSeed(int length)
|
||||
{
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
@@ -186,6 +177,8 @@ namespace Barotrauma
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
public static int IdentifierToInt(Identifier id) => StringToInt(id.Value.ToLowerInvariant());
|
||||
|
||||
public static int StringToInt(string str)
|
||||
{
|
||||
str = str.Substring(0, Math.Min(str.Length, 32));
|
||||
@@ -201,6 +194,7 @@ namespace Barotrauma
|
||||
|
||||
return BitConverter.ToInt32(asciiBytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a method for changing inputtypes with old names to the new ones to ensure backwards compatibility with older subs
|
||||
/// </summary>
|
||||
@@ -264,16 +258,17 @@ namespace Barotrauma
|
||||
{
|
||||
string color = obj switch
|
||||
{
|
||||
bool b => b ? "80,250,123" : "255,85,85",
|
||||
string _ => "241,250,140",
|
||||
int _ => "189,147,249",
|
||||
float _ => "189,147,249",
|
||||
double _ => "189,147,249",
|
||||
null => "255,85,85",
|
||||
bool b => b ? "80,250,123" : "255,85,85",
|
||||
string _ => "241,250,140",
|
||||
Identifier _ => "241,250,140",
|
||||
int _ => "189,147,249",
|
||||
float _ => "189,147,249",
|
||||
double _ => "189,147,249",
|
||||
null => "255,85,85",
|
||||
_ => "139,233,253"
|
||||
};
|
||||
|
||||
return obj is string
|
||||
return obj is string || obj is Identifier
|
||||
? $"‖color:{color}‖\"{obj}\"‖color:end‖"
|
||||
: $"‖color:{color}‖{obj ?? "null"}‖color:end‖";
|
||||
}
|
||||
@@ -352,7 +347,7 @@ namespace Barotrauma
|
||||
return d[n, m];
|
||||
}
|
||||
|
||||
public static string SecondsToReadableTime(float seconds)
|
||||
public static LocalizedString SecondsToReadableTime(float seconds)
|
||||
{
|
||||
int s = (int)(seconds % 60.0f);
|
||||
if (seconds < 60.0f)
|
||||
@@ -363,23 +358,23 @@ namespace Barotrauma
|
||||
int h = (int)(seconds / (60.0f * 60.0f));
|
||||
int m = (int)((seconds / 60.0f) % 60);
|
||||
|
||||
string text = "";
|
||||
LocalizedString text = "";
|
||||
if (h != 0) { text = TextManager.GetWithVariable("timeformathours", "[hours]", h.ToString()); }
|
||||
if (m != 0)
|
||||
{
|
||||
string minutesText = TextManager.GetWithVariable("timeformatminutes", "[minutes]", m.ToString());
|
||||
text = string.IsNullOrEmpty(text) ? minutesText : string.Join(" ", text, minutesText);
|
||||
LocalizedString minutesText = TextManager.GetWithVariable("timeformatminutes", "[minutes]", m.ToString());
|
||||
text = text.IsNullOrEmpty() ? minutesText : LocalizedString.Join(" ", text, minutesText);
|
||||
}
|
||||
if (s != 0)
|
||||
{
|
||||
string secondsText = TextManager.GetWithVariable("timeformatseconds", "[seconds]", s.ToString());
|
||||
text = string.IsNullOrEmpty(text) ? secondsText : string.Join(" ", text, secondsText);
|
||||
LocalizedString secondsText = TextManager.GetWithVariable("timeformatseconds", "[seconds]", s.ToString());
|
||||
text = text.IsNullOrEmpty() ? secondsText : LocalizedString.Join(" ", text, secondsText);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> cachedLines = new Dictionary<string, List<string>>();
|
||||
public static string GetRandomLine(string filePath, Rand.RandSync randSync = Rand.RandSync.Server)
|
||||
public static string GetRandomLine(string filePath, Rand.RandSync randSync = Rand.RandSync.ServerAndClient)
|
||||
{
|
||||
List<string> lines;
|
||||
if (cachedLines.ContainsKey(filePath))
|
||||
@@ -426,7 +421,20 @@ namespace Barotrauma
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static T SelectWeightedRandom<T>(IList<T> objects, IList<float> weights, Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
public static T SelectWeightedRandom<T>(IEnumerable<T> objects, Func<T, float> weightMethod, Rand.RandSync randSync)
|
||||
{
|
||||
return SelectWeightedRandom(objects, weightMethod, Rand.GetRNG(randSync));
|
||||
}
|
||||
|
||||
|
||||
public static T SelectWeightedRandom<T>(IEnumerable<T> objects, Func<T, float> weightMethod, Random random)
|
||||
{
|
||||
List<T> objectList = objects.ToList();
|
||||
List<float> weights = objectList.Select(o => weightMethod(o)).ToList();
|
||||
return SelectWeightedRandom(objectList, weights, random);
|
||||
}
|
||||
|
||||
public static T SelectWeightedRandom<T>(IList<T> objects, IList<float> weights, Rand.RandSync randSync)
|
||||
{
|
||||
return SelectWeightedRandom(objects, weights, Rand.GetRNG(randSync));
|
||||
}
|
||||
@@ -455,11 +463,14 @@ namespace Barotrauma
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static UInt32 IdentifierToUint32Hash(Identifier id, MD5 md5)
|
||||
=> StringToUInt32Hash(id.Value.ToLowerInvariant(), md5);
|
||||
|
||||
public static UInt32 StringToUInt32Hash(string str, MD5 md5)
|
||||
{
|
||||
//calculate key based on MD5 hash instead of string.GetHashCode
|
||||
//to ensure consistent results across platforms
|
||||
byte[] inputBytes = Encoding.ASCII.GetBytes(str);
|
||||
byte[] inputBytes = Encoding.UTF8.GetBytes(str);
|
||||
byte[] hash = md5.ComputeHash(inputBytes);
|
||||
|
||||
UInt32 key = (UInt32)((str.Length & 0xff) << 24); //could use more of the hash here instead?
|
||||
@@ -609,16 +620,6 @@ namespace Barotrauma
|
||||
return commands.ToArray();
|
||||
}
|
||||
|
||||
public static void OpenFileWithShell(string filename)
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = filename,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up a path by replacing backslashes with forward slashes, and
|
||||
/// optionally corrects the casing of the path. Recommended when serializing
|
||||
|
||||
Reference in New Issue
Block a user