SamsonGame/Assets/Scripts/Core/Extensions/Extensions.cs

888 lines
22 KiB
C#
Raw Normal View History

2021-12-29 20:50:11 +03:00
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Text;
using System.Web;
using metagen;
using game;
using Type = System.Type;
public static class Extensions
{
public static bool IsEmpty(this Vector3 self)
{
return float.IsNegativeInfinity(self.x) &&
float.IsNegativeInfinity(self.y) &&
float.IsNegativeInfinity(self.z);
}
public static Vector2 FromXZ(this Vector3 self)
{
return new Vector2(self.x, self.z);
}
public static Vector3 FromXY(this Vector2 self)
{
return new Vector3(self.x, 0, self.y);
}
public static Vector3 ToXZ(this Vector3 self)
{
return new Vector3(self.x, 0, self.z);
}
public static bool IsValid(this Vector3 v)
{
return !float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z);
}
public static bool BitIsSet(this uint self, int bit)
{
return (self & (uint)(1 << bit)) != 0;
}
public static uint BitSet(this uint self, int bit, bool flag)
{
if(flag)
{
self |= (uint)(1 << bit);
return self;
}
else
{
self &= ~(uint)(1 << bit);
return self;
}
}
public static int CopyToEx(this Stream src, Stream dest)
{
int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
byte[] buffer = new byte[size];
int total = 0;
int n;
do
{
n = src.Read(buffer, 0, buffer.Length);
dest.Write(buffer, 0, n);
total += n;
} while (n != 0);
return total;
}
public static int CopyToEx(this MemoryStream src, Stream dest)
{
int total = (int)(src.Length - src.Position);
dest.Write(src.GetBuffer(), (int)src.Position, total);
return total;
}
public static int CopyToEx(this Stream src, MemoryStream dest)
{
if(src.CanSeek)
{
int total = 0;
int pos = (int)dest.Position;
int length = (int)(src.Length - src.Position) + pos;
dest.SetLength(length);
while(pos < length)
{
int n = src.Read(dest.GetBuffer(), pos, length - pos);
total += n;
pos += n;
}
return total;
}
else
return src.CopyToEx((Stream)dest);
}
public static void SetData(this MemoryStream ms, byte[] b, int offset, int blen)
{
ms.SetLength(0);
ms.Write(b, offset, blen);
ms.Position = 0;
}
public static string Reverse(this string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public static bool ExtractVersion(this string v, out int maj, out int min, out int pat)
{
maj = 0;
min = 0;
pat = 0;
if(v == null || v.Length == 0)
return false;
string[] pieces = v.Split('.');
try
{
if(pieces.Length == 2)
{
maj = int.Parse(pieces[0]);
min = int.Parse(pieces[1]);
return true;
}
else if(pieces.Length == 3)
{
maj = int.Parse(pieces[0]);
min = int.Parse(pieces[1]);
pat = int.Parse(pieces[2]);
return true;
}
else
return false;
}
catch(Exception)
{
return false;
}
}
public static string RemoveQueryStringByKey(this string url, string key)
{
if(string.IsNullOrEmpty(url))
return url;
var uri = new Uri(url);
// this gets all the query string key value pairs as a collection
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
// this removes the key if exists
newQueryString.Remove(key);
return uri.ReplaceURIQueryString(newQueryString);
}
public static string ReplaceURIQueryString(this Uri uri, NameValueCollection newQueryString)
{
// this gets the page path from root without QueryString
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
return newQueryString.Count > 0 ?
$"{pagePathWithoutQueryString}?{newQueryString}" :
pagePathWithoutQueryString;
}
public static GameObject GetChild(this GameObject o, string name)
{
Transform t = o.transform.Find(name);
if(t == null)
Error.Verify(false, "Child not found {0}", name);
return t.gameObject;
}
public static T GetChild<T>(this GameObject o, string name) where T : Component
{
return o.GetChild(name).AsComponent<T>();
}
public static T GetChild<T>(this GameObject o) where T : Component
{
var res = o.GetComponentInChildren<T>();
VerifyComponent(o, res);
return res;
}
public static GameObject FindChild(this GameObject o, string name)
{
Transform t = o.transform.Find(name);
if(t)
return t.gameObject;
return null;
}
public static GameObject FindChildRecursive(this GameObject o, string name)
{
Transform t = o.transform.FindRecursive(name);
if(t == null)
return null;
return t.gameObject;
}
public static Transform GetChild(this Transform o, string name)
{
Transform t = o.transform.Find(name);
if(t == null)
Error.Verify(false, "Child not found {0}", name);
return t;
}
public static void SetActiveAllChildren(this GameObject o, bool flag)
{
for(int i=0;i<o.transform.childCount;++i)
o.transform.GetChild(i).gameObject.SetActive(flag);
}
public static string GetFullPath(this GameObject o)
{
return o.transform.GetFullPath();
}
public static void SetLayerRecursive(this GameObject obj, int layer, int except_layer = -1)
{
if(obj.layer == except_layer)
return;
obj.layer = layer;
for(int i = 0; i < obj.transform.childCount; ++i)
SetLayerRecursive(obj.transform.GetChild(i).gameObject, layer, except_layer);
}
//NOTE: using Unity's built-in non-allocating Find
public static Transform FindRecursive(this Transform current, string name)
{
if(current.parent)
{
if(current.parent.Find(name) == current)
return current;
}
//NOTE: switching to mem-allocating version only if there's no parent
else if(current.name == name)
return current;
for(int i = 0; i < current.childCount; ++i)
{
var chld = current.GetChild(i);
var tmp = chld.FindRecursive(name);
if(tmp != null)
return tmp;
}
return null;
}
public static GameObject GetParent(this GameObject o)
{
return o.transform.parent.gameObject;
}
public static string GetFullPath(this Transform t)
{
return t.GetPathWhile(current => current != null);
}
public delegate bool TransformPathBuilderCondition(Transform current);
public static string GetPathWhile(this Transform t, TransformPathBuilderCondition condition)
{
string path = "";
Transform tmp = t;
while(condition(tmp))
{
path = tmp.gameObject.name + (path.Length > 0 ? ("/" + path) : "");
tmp = tmp.parent;
}
return path;
}
public static bool BelongsTo(this GameObject o, GameObject other)
{
Transform current = o.transform;
while(current != null)
{
if(current.gameObject == other)
return true;
current = current.parent;
}
return false;
}
public static string EscapeSlash(this string src)
{
return src.Replace('/', '\u2215');
}
public struct ComponentsList
{
public bool busy;
public object list;
}
//generic cache of component lists
static List<ComponentsList> comp_lists = new List<ComponentsList>();
public static List<T> RequestComponentsList<T>() where T : Component
{
//1. try to find non-busy existing one
{
for(int i=0;i<comp_lists.Count;++i)
{
var cl = comp_lists[i];
if(cl.busy)
continue;
var lst = cl.list as List<T>;
if(lst == null)
continue;
cl.busy = true;
comp_lists[i] = cl;
return lst;
}
}
//2. otherwise create new one
{
var cl = new ComponentsList();
var lst = new List<T>();
cl.list = lst;
cl.busy = true;
comp_lists.Add(cl);
return lst;
}
}
public static void ReleaseComponentsList<T>(List<T> lst) where T : Component
{
for(int i=0;i<comp_lists.Count;++i)
{
var cl = comp_lists[i];
if(cl.list == lst)
{
lst.Clear();
cl.busy = false;
comp_lists[i] = cl;
return;
}
}
}
public static GameObject CloneTpl(this GameObject tpl)
{
GameObject o = GameObject.Instantiate(tpl) as GameObject;
o.transform.SetParent(tpl.transform.parent);
o.transform.localPosition = tpl.transform.localPosition;
o.transform.localRotation = tpl.transform.localRotation;
o.transform.localScale = tpl.transform.localScale;
return o;
}
public static void GetComponentsRecursive<T>(this Component self, List<T> res) where T : Component
{
self.GetComponentsInChildren<T>(includeInactive: true, result: res);
}
public static T GetFirstComponent<T>(this Component self) where T : Component
{
T c = self.GetComponent<T>();
if(c != null)
return c;
var tfm = self.transform;
for(int i = 0; i < tfm.childCount; ++i)
{
var cc = tfm.GetChild(i).GetFirstComponent<T>();
if(cc != null)
return cc;
}
return c;
}
public static T AddComponentOnce<T>(this GameObject self) where T : Component
{
T c = self.GetComponent<T>();
if(c == null)
c = self.AddComponent<T>();
return c;
}
public static T AsComponent<T>(this GameObject o) where T : Component
{
var res = o.GetComponent<T>();
VerifyComponent(o, res);
return res;
}
static void VerifyComponent<T>(GameObject o, T res) where T : Component
{
Error.Verify(res != null, "Object {0} must have component {1}\n", o.GetFullPath(), typeof(T).Name);
}
public static void AddRendererSortingOrderRecursive(this Transform tfm, int add_order, string layer = null)
{
var r = tfm.GetComponent<Renderer>();
if(r != null)
{
if(layer != null)
r.sortingLayerName = layer;
r.sortingOrder += add_order;
}
for(int i=0;i<tfm.childCount;++i)
tfm.GetChild(i).AddRendererSortingOrderRecursive(add_order, layer);
}
public static void SetRendererSortingOrderRecursive(this Transform tfm, int order, string layer = null)
{
var r = tfm.GetComponent<Renderer>();
if(r != null)
{
if(layer != null)
r.sortingLayerName = layer;
r.sortingOrder = order;
}
for(int i=0;i<tfm.childCount;++i)
tfm.GetChild(i).SetRendererSortingOrderRecursive(order, layer);
}
public static void UpdateRendererSortingOrderRecursive(this Transform tfm, int base_order, string layer = null)
{
var r = tfm.GetComponent<Renderer>();
if(r != null)
{
if(layer != null)
r.sortingLayerName = layer;
r.sortingOrder += base_order;
}
for(int i=0;i<tfm.childCount;++i)
tfm.GetChild(i).UpdateRendererSortingOrderRecursive(base_order, layer);
}
static HashSet<object> mem_history = new HashSet<object>();
public static int GetApproxMemSize(this object obj, int str_symb_size = 2, bool is_top = true)
{
if(is_top)
mem_history.Clear();
if(mem_history.Contains(obj))
return 0;
mem_history.Add(obj);
int ptr_size = IntPtr.Size;
if(obj == null)
return ptr_size;
int size = 0;
Type type = obj.GetType();
var info = type.GetFields(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic
);
foreach(var field in info)
{
if(field.FieldType.IsEnum)
size += 4; //????
else if(field.FieldType.IsValueType)
size += System.Runtime.InteropServices.Marshal.SizeOf(field.FieldType);
else
{
size += ptr_size;
if(field.FieldType.IsArray)
{
var array = field.GetValue(obj) as Array;
if(array != null)
{
var elementType = array.GetType().GetElementType();
if(elementType.IsValueType)
size += System.Runtime.InteropServices.Marshal.SizeOf(elementType) * array.Length;
else
{
size += ptr_size * array.Length;
if(elementType == typeof(string))
size += str_symb_size * array.Length;
}
}
}
else if(field.FieldType == typeof(string) && field.GetValue(obj) != null)
size += (field.GetValue(obj) as string).Length * str_symb_size;
else
size += field.GetValue(obj).GetApproxMemSize(str_symb_size, false);
}
}
return size;
}
public static readonly DateTime UnixEpoch = new DateTime(1970,1,1,0,0,0,0,DateTimeKind.Utc);
public static double ToStamp(this DateTime date)
{
return date.ToUniversalTime().Subtract(UnixEpoch).TotalSeconds;
}
public static DateTime ToDateTime(this double unix_stamp)
{
return UnixEpoch.AddSeconds(unix_stamp).ToLocalTime();
}
public static DateTime ToDateTimeUTC(this double unix_stamp)
{
return UnixEpoch.AddSeconds(unix_stamp).ToUniversalTime();
}
public static DateTime ToDateTime(this uint unix_stamp)
{
return ToDateTime((double)unix_stamp);
}
public static DateTime ToDateTimeUTC(this uint unix_stamp)
{
return ToDateTimeUTC((double)unix_stamp);
}
public static DateTime ToDateTime(this CodeStage.AntiCheat.ObscuredTypes.ObscuredUInt unix_stamp)
{
return ToDateTime((uint)unix_stamp);
}
public static bool IsSameDay(this DateTime date_time, DateTime other)
{
return date_time.Year == other.Year && date_time.Month == other.Month && date_time.Day == other.Day;
}
//public static uint CalcHash(this ConfBase conf)
//{
// //NOTE: we want to ignore id related changes
// uint backup_id = conf.id;
// string backup_strid = conf.strid;
// conf.id = 0;
// conf.strid = "";
// uint hash = MetaData.CalcHash(conf);
// conf.id = backup_id;
// conf.strid = backup_strid;
// return hash;
//}
public static DateTime Max(DateTime lhs, DateTime rhs)
{
return lhs > rhs ? lhs : rhs;
}
public static TimeSpan Max(TimeSpan lhs, TimeSpan rhs)
{
return lhs > rhs ? lhs : rhs;
}
public static bool IsValid(this DateTime date)
{
var reference = UnixEpoch;
if(date.Kind == DateTimeKind.Local)
reference = reference.ToLocalTime();
return date.Ticks > reference.Ticks; //TODO?
}
public static string GetSceneName(this ConfBase self)
{
return self.strid.Replace("@", "").Replace("/", "_");
}
public static Color AsColor(this int n)
{
uint val = (uint)n;
// scramble the bits up using Robert Jenkins' 32 bit integer hash function
val = (val+0x7ed55d16) + (val<<12);
val = (val^0xc761c23c) ^ (val>>19);
val = (val+0x165667b1) + (val<<5);
val = (val+0xd3a2646c) ^ (val<<9);
val = (val+0xfd7046c5) + (val<<3);
val = (val^0xb55a4f09) ^ (val>>16);
float r = (float)((val>>0) & 0xFF);
float g = (float)((val>>8) & 0xFF);
float b = (float)((val>>16) & 0xFF);
float max = (float)Mathf.Max(Mathf.Max(r, g), b);
float min = (float)Mathf.Min(Mathf.Min(r, g), b);
float intensity = 0.75f;
// Saturate and scale the color
if(min == max)
{
return new Color(intensity, 0.0f, 0.0f, 1.0f);
}
else
{
float coef = (float)intensity/(max - min);
return new Color(
(r - min)*coef,
(g - min)*coef,
(b - min)*coef,
1.0f
);
}
}
public static string Trace<T>(this IList<T> list)
{
string res = "<empty list>";
for(int i=0; i<list.Count; ++i)
{
string asstr = list[i] == null ? "<null>" : list[i].ToString();
res = i == 0 ? asstr : TempStringBuffer.Concat(res, ", ", asstr);
}
return res;
}
public static string Trace<K, V>(this Dictionary<K, V> dict, string separator = ": ")
{
string res = "";
foreach(var pair in dict)
{
string asstr = $"{pair.Key}{separator}{pair.Value}";
res = string.IsNullOrEmpty(res) ? asstr : TempStringBuffer.Concat(res, ", ", asstr);
}
return res;
}
public static V GetOr<K, V>(this IDictionary<K, V> dict, K key, V default_val)
{
V res;
if(!dict.TryGetValue(key, out res))
res = default_val;
return res;
}
//NOTE: T should be Enum. There is no 'official' Enum constraint in C#
public static T ParseEnum<T>(this string enum_val) where T : struct, IConvertible
{
return (T)System.Enum.Parse(typeof(T), enum_val);
}
//NOTE: T should be Enum. There is no 'official' Enum constraint in C#
public static T RotateEnum<T>(this T enum_val) where T : struct, IConvertible
{
int int_val = (int)(object)enum_val;
int_val++;
enum_val = (T)(object)int_val;
if(!Enum.IsDefined(typeof(T), enum_val))
enum_val = (T)(object)0;
return enum_val;
}
//NOTE: this method contains boxing allocation, use with caution
public static string ToEnumName<T>(this int raw_enum_val) where T : struct, IConvertible
{
return Enum.GetName(typeof(T), raw_enum_val);
}
//
public static bool ToBool(this byte val)
{
return val > 0;
}
public static byte ToByte(this bool val)
{
return (byte)(val ? 1 : 0);
}
public static void SelectChild(this GameObject obj, string name)
{
foreach(Transform tfm in obj.transform)
{
GameObject child = tfm.gameObject;
child.SetActive(child.name == name);
}
}
public static void ChildSetActive(this GameObject obj, bool activate)
{
foreach(Transform tfm in obj.transform)
tfm.gameObject.SetActive(activate);
}
public static string ShortAlias(this ConfBase proto)
{
string stats_str = proto.strid;
return stats_str.BaseName();
}
public static string BaseName(this string stats_str)
{
return stats_str.Substring(stats_str.LastIndexOf("/") + 1); // name only without path to conf
}
public static string StatsAlias(this ConfBase proto)
{
return proto.strid.Substring(1); //omit '@'
}
public static string StatsAliasWithout(this ConfBase proto, string to_remove)
{
return proto.StatsAlias().Replace(to_remove, "");
}
public static uint SafeId(this ConfBase proto)
{
return proto == null ? 0 : proto.id;
}
public static string ReplaceFirst(this string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
return text;
return $"{text.Substring(0, pos)}{replace}{text.Substring(pos + search.Length)}";
}
public static void RestoreLocalSettingsToDefault(this Transform transform)
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
public static void InvertVectors(ref Vector3 vector_a, ref Vector3 vector_b)
{
var temp_start_pos = vector_a;
vector_a = vector_b;
vector_b = temp_start_pos;
}
// public static T RandomValue<T>(this List<T> list) where T : class
// {
// if(list.Count == 0)
// return null;
// return list[UnityEngine.Random.Range(0, list.Count)];
// }
//
// public static uint RandomValue(this List<uint> list)
// {
// if(list.Count == 0)
// return 0;
// return list[UnityEngine.Random.Range(0, list.Count)];
// }
public static int Max(this int[] array)
{
if(array.Length == 0)
return 0;
int max = array[0];
for(int i = 0; i < array.Length; ++i)
if(max < array[i])
max = array[i];
return max;
}
public static float GetMaxX(this List<Vector3> vectors_list)
{
var max_x = float.MinValue;
foreach(var vector in vectors_list)
if(vector.x > max_x)
max_x = vector.x;
return max_x;
}
public static float GetMaxY(this List<Vector3> vectors_list)
{
var max_y = float.MinValue;
foreach(var vector in vectors_list)
if(vector.y > max_y)
max_y = vector.y;
return max_y;
}
public static void SetAlpha(this Image self, float alpha)
{
var color = self.color;
color.a = alpha;
self.color = color;
}
public static string UnescapeName(this string str, int NAME_MAX_LENGTH)
{
string res = str.Unescape();
int len = Math.Min(res.Length, NAME_MAX_LENGTH);
res = res.Substring(0, len);
return res;
}
public static string Unescape(this string str)
{
try
{
return System.Text.RegularExpressions.Regex.Unescape(str);
}
catch(Exception)
{
Log.Error("Could not unescape string '" + str + "'");
return "???";
}
}
public static bool IsStorageFullException(this Exception e)
{
const long ERROR_HANDLE_DISK_FULL = 0x27;
const long ERROR_DISK_FULL = 0x70;
long hr = Marshal.GetHRForException(e) & 0xFFFF;
return hr == ERROR_HANDLE_DISK_FULL || hr == ERROR_DISK_FULL;
}
public static void SimulateStorageFullException()
{
//const int HR_ERROR_HANDLE_DISK_FULL = unchecked((int)0x80070027);
const int HR_ERROR_DISK_FULL = unchecked((int)0x80070070);
var ioe = new IOException("Pretending that storage is full!", HR_ERROR_DISK_FULL);
throw ioe;
}
public static string Truncate(this string str, int max_length)
{
if(string.IsNullOrEmpty(str))
return str;
return str.Length <= max_length ? str : str.Substring(0, max_length);
}
public static Sprite GetSprite(this GameObject go)
{
if(go == null)
return null;
var sr = go.transform.GetFirstComponent<SpriteRenderer>();
return sr == null ? null : sr.sprite;
}
public static long ParseStockAmount(this string amount_str)
{
//NOTE: a hopefully bulletproof parsing that will floor non-integer amounts sent by 3rd-parties
return (long)double.Parse(amount_str, System.Globalization.CultureInfo.InvariantCulture);
}
public static void MaskSet(this ref long mask, int bit, bool val)
{
if(val)
mask |= (long)(1 << bit);
else
mask &= ~(1 << bit);
}
public static bool MaskIsSet(this long mask, int bit)
{
return (mask & 1 << bit) != 0;
}
public static Color SetAlpha(this Color color, float value)
{
color.a = value;
return color;
}
}