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(this GameObject o, string name) where T : Component { return o.GetChild(name).AsComponent(); } public static T GetChild(this GameObject o) where T : Component { var res = o.GetComponentInChildren(); 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 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 comp_lists = new List(); public static List RequestComponentsList() where T : Component { //1. try to find non-busy existing one { for(int i=0;i; 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(); cl.list = lst; cl.busy = true; comp_lists.Add(cl); return lst; } } public static void ReleaseComponentsList(List lst) where T : Component { for(int i=0;i(this Component self, List res) where T : Component { self.GetComponentsInChildren(includeInactive: true, result: res); } public static T GetFirstComponent(this Component self) where T : Component { T c = self.GetComponent(); if(c != null) return c; var tfm = self.transform; for(int i = 0; i < tfm.childCount; ++i) { var cc = tfm.GetChild(i).GetFirstComponent(); if(cc != null) return cc; } return c; } public static T AddComponentOnce(this GameObject self) where T : Component { T c = self.GetComponent(); if(c == null) c = self.AddComponent(); return c; } public static T AsComponent(this GameObject o) where T : Component { var res = o.GetComponent(); VerifyComponent(o, res); return res; } static void VerifyComponent(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(); if(r != null) { if(layer != null) r.sortingLayerName = layer; r.sortingOrder += add_order; } for(int i=0;i(); if(r != null) { if(layer != null) r.sortingLayerName = layer; r.sortingOrder = order; } for(int i=0;i(); if(r != null) { if(layer != null) r.sortingLayerName = layer; r.sortingOrder += base_order; } for(int i=0;i mem_history = new HashSet(); 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(this IList list) { string res = ""; for(int i=0; i" : list[i].ToString(); res = i == 0 ? asstr : TempStringBuffer.Concat(res, ", ", asstr); } return res; } public static string Trace(this Dictionary 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(this IDictionary 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(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(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(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(this List list) where T : class // { // if(list.Count == 0) // return null; // return list[UnityEngine.Random.Range(0, list.Count)]; // } // // public static uint RandomValue(this List 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 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 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(); 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; } }