Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LCBridge v1.1.2
BepInEx/plugins/LCBridge.dll
Decompiled 4 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LCBridge.Patches; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LCBridge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("LCBridge")] [assembly: AssemblyTitle("LCBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LCBridge { public static class BridgeServer { private static TcpListener _listener; private static Thread _acceptThread; private static volatile bool _running; private static readonly List<TcpClient> _clients = new List<TcpClient>(); private static readonly object _lock = new object(); public static void Start(int port) { if (!_running) { _running = true; _listener = new TcpListener(IPAddress.Loopback, port); _listener.Start(); _acceptThread = new Thread(AcceptLoop) { IsBackground = true, Name = "LCBridge-Accept" }; _acceptThread.Start(); } } public static void Stop() { _running = false; try { _listener?.Stop(); } catch { } lock (_lock) { foreach (TcpClient client in _clients) { try { client.Close(); } catch { } } _clients.Clear(); } } private static void AcceptLoop() { while (_running) { try { TcpClient tcpClient = _listener.AcceptTcpClient(); if (Handshake(tcpClient)) { lock (_lock) { _clients.Add(tcpClient); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Оверлей подключился к мосту."); } } else { try { tcpClient.Close(); } catch { } } } catch { if (!_running) { break; } } } } private static bool Handshake(TcpClient client) { try { NetworkStream stream = client.GetStream(); byte[] array = new byte[4096]; int num = stream.Read(array, 0, array.Length); if (num <= 0) { return false; } string text = Encoding.UTF8.GetString(array, 0, num); string text2 = null; string[] array2 = text.Split(new string[1] { "\r\n" }, StringSplitOptions.None); foreach (string text3 in array2) { if (text3.StartsWith("Sec-WebSocket-Key:", StringComparison.OrdinalIgnoreCase)) { text2 = text3.Substring("Sec-WebSocket-Key:".Length).Trim(); break; } } if (text2 == null) { return false; } string text4; using (SHA1 sHA = SHA1.Create()) { text4 = Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(text2 + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))); } string s = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + text4 + "\r\n\r\n"; byte[] bytes = Encoding.UTF8.GetBytes(s); stream.Write(bytes, 0, bytes.Length); return true; } catch { return false; } } public static void Broadcast(string text) { byte[] array = EncodeTextFrame(text); List<TcpClient> list = null; lock (_lock) { foreach (TcpClient client in _clients) { try { if (!client.Connected) { (list ?? (list = new List<TcpClient>())).Add(client); } else { client.GetStream().Write(array, 0, array.Length); } } catch { (list ?? (list = new List<TcpClient>())).Add(client); } } if (list == null) { return; } foreach (TcpClient item in list) { _clients.Remove(item); try { item.Close(); } catch { } } } } private static byte[] EncodeTextFrame(string text) { byte[] bytes = Encoding.UTF8.GetBytes(text); int num = bytes.Length; byte[] array; if (num <= 125) { array = new byte[2] { 0, (byte)num }; } else if (num <= 65535) { array = new byte[4] { 0, 126, (byte)((num >> 8) & 0xFF), (byte)(num & 0xFF) }; } else { array = new byte[10] { 0, 127, 0, 0, 0, 0, 0, 0, 0, 0 }; for (int i = 0; i < 8; i++) { array[9 - i] = (byte)((num >> 8 * i) & 0xFF); } } array[0] = 129; byte[] array2 = new byte[array.Length + bytes.Length]; Buffer.BlockCopy(array, 0, array2, 0, array.Length); Buffer.BlockCopy(bytes, 0, array2, array.Length, bytes.Length); return array2; } } public class BridgeTicker : MonoBehaviour { private float _timer; private const float Interval = 1f; private string _lastPayload; private int _lastMobCount = -1; private void Update() { _timer += Time.deltaTime; if (!(_timer < 1f)) { _timer = 0f; GameState.TickStats(); RunStats.Tick(); string text = BuildJson(); if (text != _lastPayload) { _lastPayload = text; BridgeServer.Broadcast(text); } } } private string BuildJson() { (int alive, int total) crew = GameState.GetCrew(); int item = crew.alive; int item2 = crew.total; int deaths = GameState.GetDeaths(); int localHealth = GameState.GetLocalHealth(); string moonName = GameState.GetMoonName(); string weatherTweaksWeather = GameState.GetWeatherTweaksWeather(); string s = ((!string.IsNullOrEmpty(weatherTweaksWeather)) ? weatherTweaksWeather : GameState.GetVanillaWeather()); string brutalEvent = GameState.GetBrutalEvent(); (List<string> outside, List<string> inside) monsters = GameState.GetMonsters(); List<string> item3 = monsters.outside; List<string> item4 = monsters.inside; List<string> traps = GameState.GetTraps(); bool onMoon = GameState.GetOnMoon(); bool loading = GameState.GetLoading(); bool inGame = GameState.GetInGame(); int resetToken = GameState.GetResetToken(); int levelScrap = GameState.GetLevelScrap(); string topKiller = GameState.GetTopKiller(); string topMonster = GameState.GetTopMonster(); string deadliestEvent = GameState.GetDeadliestEvent(); int num = item3.Count + item4.Count; if (num != _lastMobCount) { _lastMobCount = num; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[monsters] улица={0} ({1}) | комплекс={2} ({3})", item3.Count, string.Join(",", item3), item4.Count, string.Join(",", item4))); } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); stringBuilder.Append("\"type\":\"bridge\","); stringBuilder.Append("\"deaths\":").Append(deaths).Append(','); stringBuilder.Append("\"alive\":").Append(item).Append(','); stringBuilder.Append("\"total\":").Append(item2).Append(','); stringBuilder.Append("\"health\":").Append(localHealth).Append(','); stringBuilder.Append("\"moonName\":").Append(JsonStr(moonName)).Append(','); stringBuilder.Append("\"weatherFull\":").Append(JsonStr(s)).Append(','); stringBuilder.Append("\"brutalEvent\":").Append(JsonStr(brutalEvent ?? "")).Append(','); stringBuilder.Append("\"onMoon\":").Append(onMoon ? "true" : "false").Append(','); stringBuilder.Append("\"loading\":").Append(loading ? "true" : "false").Append(','); stringBuilder.Append("\"inGame\":").Append(inGame ? "true" : "false").Append(','); stringBuilder.Append("\"resetToken\":").Append(resetToken).Append(','); stringBuilder.Append("\"levelScrap\":").Append(levelScrap).Append(','); stringBuilder.Append("\"topKiller\":").Append(JsonStr(topKiller ?? "")).Append(','); stringBuilder.Append("\"topMonster\":").Append(JsonStr(topMonster ?? "")).Append(','); stringBuilder.Append("\"deadliestEvent\":").Append(JsonStr(deadliestEvent ?? "")).Append(','); stringBuilder.Append("\"monstersOutside\":").Append(JsonArr(item3)).Append(','); stringBuilder.Append("\"monstersInside\":").Append(JsonArr(item4)).Append(','); stringBuilder.Append("\"traps\":").Append(JsonArr(traps)).Append(','); stringBuilder.Append("\"run\":").Append(RunStats.ToJson()); stringBuilder.Append('}'); return stringBuilder.ToString(); } private static string JsonArr(List<string> items) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('['); for (int i = 0; i < items.Count; i++) { if (i > 0) { stringBuilder.Append(','); } stringBuilder.Append(JsonStr(items[i])); } stringBuilder.Append(']'); return stringBuilder.ToString(); } private static string JsonStr(string s) { if (s == null) { return "\"\""; } StringBuilder stringBuilder = new StringBuilder(s.Length + 2); stringBuilder.Append('"'); foreach (char c in s) { switch (c) { case '"': stringBuilder.Append("\\\""); continue; case '\\': stringBuilder.Append("\\\\"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\t': stringBuilder.Append("\\t"); continue; } if (c < ' ') { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("x4", CultureInfo.InvariantCulture)); } else { stringBuilder.Append(c); } } stringBuilder.Append('"'); return stringBuilder.ToString(); } } internal static class EnemyResolver { private static readonly string[] _turretMarkers = new string[3] { "ToilHeadController", "MantiToilController", "TurretHeadController" }; private static readonly string[] _slayerMarkers = new string[3] { "ToilSlayerController", "MantiSlayerController", "SlayerController" }; public static string Resolve(object enemyAiObj) { string baseName = GetBaseName(enemyAiObj); if (string.IsNullOrEmpty(baseName)) { return null; } try { switch (TurretKind(enemyAiObj)) { case 2: return baseName + "+Turret+Slayer"; case 1: return baseName + "+Turret"; } } catch { } return baseName; } private static string GetBaseName(object enemyAiObj) { try { EnemyAI val = (EnemyAI)((enemyAiObj is EnemyAI) ? enemyAiObj : null); if ((Object)(object)val != (Object)null && (Object)(object)val.enemyType != (Object)null && !string.IsNullOrEmpty(val.enemyType.enemyName)) { return val.enemyType.enemyName; } } catch { } return null; } private static int TurretKind(object enemyAiObj) { MonoBehaviour val = (MonoBehaviour)((enemyAiObj is MonoBehaviour) ? enemyAiObj : null); if ((Object)(object)val == (Object)null) { return 0; } Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(true); if (componentsInChildren == null) { return 0; } int num = 0; Component[] array = componentsInChildren; foreach (Component val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } string name = ((object)val2).GetType().Name; if (string.IsNullOrEmpty(name)) { continue; } string[] slayerMarkers = _slayerMarkers; foreach (string value in slayerMarkers) { if (name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return 2; } } if (num != 0) { continue; } slayerMarkers = _turretMarkers; foreach (string value2 in slayerMarkers) { if (name.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { num = 1; break; } } } return num; } } public static class GameState { private static int _deaths = 0; private static readonly HashSet<int> _deadThisRound = new HashSet<int>(); private static int _resetToken = 0; private static readonly Dictionary<string, int> _killerCounts = new Dictionary<string, int>(); private static readonly Dictionary<string, int> _eventDeaths = new Dictionary<string, int>(); private static readonly Dictionary<string, int> _monsterSeen = new Dictionary<string, int>(); private static int _landedScrap; private static bool _wasLanded; private static bool _scrapLocked; private static Type _emType; private static FieldInfo _curEventsField; private static bool _bcSearched; private static readonly Dictionary<Type, MethodInfo> _nameMethodCache = new Dictionary<Type, MethodInfo>(); private static Type _wtVarsType; private static MethodInfo _wtGetCurrent; private static bool _wtSearched; public static int GetResetToken() { return _resetToken; } public static void RegisterDeath(PlayerControllerB p, string killer = null) { bool flag = false; try { int item = (int)p.playerClientId; if (_deadThisRound.Add(item)) { _deaths++; flag = true; } } catch { _deaths++; flag = true; } if (!flag) { return; } try { if (!string.IsNullOrEmpty(killer)) { _killerCounts.TryGetValue(killer, out var value); _killerCounts[killer] = value + 1; } string brutalEvent = GetBrutalEvent(); if (!string.IsNullOrEmpty(brutalEvent) && brutalEvent != "—") { _eventDeaths.TryGetValue(brutalEvent, out var value2); _eventDeaths[brutalEvent] = value2 + 1; } RunStats.OnDeath(killer); } catch { } } public static void OnNewRound() { _deadThisRound.Clear(); } public static void ResetDeaths() { _deaths = 0; _deadThisRound.Clear(); _resetToken++; _killerCounts.Clear(); _eventDeaths.Clear(); _monsterSeen.Clear(); RunStats.ResetRun(); } public static int GetDeaths() { return _deaths; } public static void TickStats() { try { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead) { string text = EnemyResolver.Resolve(spawnedEnemy); if (text != null) { hashSet.Add(text); } } } foreach (string item in hashSet) { _monsterSeen.TryGetValue(item, out var value); _monsterSeen[item] = value + 1; } } catch { } } private static string TopOf(Dictionary<string, int> dict) { string result = null; int num = 0; foreach (KeyValuePair<string, int> item in dict) { if (item.Value > num) { num = item.Value; result = item.Key; } } return result; } public static string GetTopKiller() { return TopOf(_killerCounts); } public static string GetTopMonster() { return TopOf(_monsterSeen); } public static string GetDeadliestEvent() { return TopOf(_eventDeaths); } public static (int alive, int total) GetCrew() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return (alive: 0, total: 0); } int num = 0; int num2 = 0; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && (val.isPlayerControlled || val.isPlayerDead)) { num++; if (!val.isPlayerDead) { num2++; } } } if (num == 0) { num = 1; } return (alive: num2, total: num); } catch { return (alive: 0, total: 0); } } public static int GetLocalHealth() { try { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return 0; } return val.health; } catch { return 0; } } public static (List<string> outside, List<string> inside) GetMonsters() { List<string> list = new List<string>(); List<string> list2 = new List<string>(); try { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return (outside: list, inside: list2); } Dictionary<string, int> dictionary = new Dictionary<string, int>(); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if ((Object)(object)spawnedEnemy == (Object)null || spawnedEnemy.isEnemyDead) { continue; } string key = "Unknown"; try { string text = EnemyResolver.Resolve(spawnedEnemy); if (!string.IsNullOrEmpty(text)) { key = text; } } catch { } Dictionary<string, int> obj2 = (spawnedEnemy.isOutside ? dictionary : dictionary2); obj2.TryGetValue(key, out var value); obj2[key] = value + 1; } foreach (KeyValuePair<string, int> item in dictionary) { list.Add((item.Value > 1) ? $"{item.Key} x{item.Value}" : item.Key); } foreach (KeyValuePair<string, int> item2 in dictionary2) { list2.Add((item2.Value > 1) ? $"{item2.Key} x{item2.Value}" : item2.Key); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("GetMonsters fail: " + ex.Message)); } } return (outside: list, inside: list2); } public static List<string> GetTraps() { List<string> list = new List<string>(); try { Dictionary<string, int> counts = new Dictionary<string, int>(); CountType<Turret>("Turret"); CountType<Landmine>("Landmine"); CountType<SpikeRoofTrap>("Spike Trap"); foreach (KeyValuePair<string, int> item in counts) { list.Add((item.Value > 1) ? $"{item.Key} x{item.Value}" : item.Key); } void CountType<T>(string label) where T : Object { try { T[] array = Object.FindObjectsOfType<T>(); if (array != null && array.Length != 0) { counts.TryGetValue(label, out var value); counts[label] = value + array.Length; } } catch { } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("GetTraps fail: " + ex.Message)); } } return list; } public static int GetLevelScrap() { try { RoundManager instance = RoundManager.Instance; if (GetOnMoon()) { if (!_wasLanded) { _wasLanded = true; _scrapLocked = false; _landedScrap = 0; } if (!_scrapLocked && (Object)(object)instance != (Object)null) { int num = (int)instance.totalScrapValueInLevel; if (num > 0) { _landedScrap = num; _scrapLocked = true; } } } else { _wasLanded = false; _scrapLocked = false; _landedScrap = 0; } return _landedScrap; } catch { return _landedScrap; } } private static string GetEventName(object ev) { Type type = ev.GetType(); if (!_nameMethodCache.TryGetValue(type, out var value)) { value = type.GetMethod("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _nameMethodCache[type] = value; } if (value != null) { try { return value.Invoke(ev, null) as string; } catch { } } return ExtractName(ev); } public static string GetBrutalEvent() { try { if (!_bcSearched) { _bcSearched = true; _emType = FindTypeByFullName("BrutalCompanyMinus.Minus.EventManager") ?? FindTypeFuzzy("BrutalCompany", new string[1] { "EventManager" }); if (_emType != null) { _curEventsField = _emType.GetField("currentEvents", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[reflection] BCMER EventManager=" + _emType.FullName + ", currentEvents field=" + ((_curEventsField != null) ? "OK" : "НЕ НАЙДЕНО"))); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[reflection] BCMER EventManager не найден (мод выключен?)"); } } } if (_curEventsField == null) { return null; } if (!(_curEventsField.GetValue(null) is IEnumerable enumerable)) { return null; } List<string> list = new List<string>(); foreach (object item in enumerable) { if (item != null) { string eventName = GetEventName(item); if (!string.IsNullOrEmpty(eventName)) { list.Add(eventName); } } } if (list.Count == 0) { return null; } return string.Join(", ", list); } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)("GetBrutalEvent fail: " + ex.Message)); } return null; } } public static string GetWeatherTweaksWeather() { try { if (!_wtSearched) { _wtSearched = true; _wtVarsType = FindTypeByFullName("WeatherTweaks.Variables") ?? FindTypeFuzzy("WeatherTweaks", new string[1] { "Variables" }); if (_wtVarsType != null) { _wtGetCurrent = _wtVarsType.GetMethod("GetCurrentWeather", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[reflection] WeatherTweaks Variables=" + _wtVarsType.FullName + ", GetCurrentWeather=" + ((_wtGetCurrent != null) ? "OK" : "НЕ НАЙДЕНО"))); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[reflection] WeatherTweaks Variables не найден (мод выключен?)"); } } } if (_wtGetCurrent == null) { return null; } object obj = _wtGetCurrent.Invoke(null, null); if (obj == null) { return null; } string text = ExtractName(obj); return string.IsNullOrEmpty(text) ? null : text; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)("GetWeatherTweaks fail: " + ex.Message)); } return null; } } public static string GetVanillaWeather() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null) { return "None"; } return ((object)Unsafe.As<LevelWeatherType, LevelWeatherType>(ref instance.currentLevel.currentWeather)/*cast due to .constrained prefix*/).ToString(); } catch { return "None"; } } public static string GetMoonName() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null) { return "—"; } return instance.currentLevel.PlanetName; } catch { return "—"; } } public static bool GetOnMoon() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null) { return false; } if (!instance.shipHasLanded) { return false; } string text = (instance.currentLevel.PlanetName ?? "").ToLowerInvariant(); if (text.Contains("gordion") || text.Contains("company")) { return false; } return true; } catch { return false; } } public static bool GetLoading() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } return instance.travellingToNewLevel; } catch { return false; } } public static bool GetInGame() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } return instance.shipHasLanded || instance.travellingToNewLevel; } catch { return false; } } public static int GetDayCount() { try { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null) { return 1; } return (instance.daysUntilDeadline < 0) ? 1 : (3 - instance.daysUntilDeadline); } catch { return 1; } } public static int GetQuotaIndexSafe() { try { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null) { return 1; } return instance.timesFulfilledQuota + 1; } catch { return 1; } } public static int GetShipScrapSafe() { try { int num = 0; GameObject[] array = GameObject.FindGameObjectsWithTag("PhysicsProp"); for (int i = 0; i < array.Length; i++) { GrabbableObject component = array[i].GetComponent<GrabbableObject>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.itemProperties == (Object)null) && component.itemProperties.isScrap && (component.isInShipRoom || component.isInElevator)) { num += component.scrapValue; } } return num; } catch { return 0; } } public static List<KeyValuePair<int, int>> GetShipScrapItems() { List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(); try { GameObject[] array = GameObject.FindGameObjectsWithTag("PhysicsProp"); for (int i = 0; i < array.Length; i++) { GrabbableObject component = array[i].GetComponent<GrabbableObject>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.itemProperties == (Object)null) && component.itemProperties.isScrap && (component.isInShipRoom || component.isInElevator)) { list.Add(new KeyValuePair<int, int>(((Object)component).GetInstanceID(), component.scrapValue)); } } } catch { } return list; } public static List<KeyValuePair<int, string>> GetMonsterInstances() { List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>(); try { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return list; } foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead) { string text = EnemyResolver.Resolve(spawnedEnemy); if (text != null) { list.Add(new KeyValuePair<int, string>(((Object)spawnedEnemy).GetInstanceID(), text)); } } } } catch { } return list; } public static (List<string> outside, List<string> inside) GetMonstersRaw() { List<string> list = new List<string>(); List<string> list2 = new List<string>(); try { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return (outside: list, inside: list2); } foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead) { string text = EnemyResolver.Resolve(spawnedEnemy); if (text != null) { (spawnedEnemy.isOutside ? list : list2).Add(text); } } } } catch { } return (outside: list, inside: list2); } public static bool GetInsideFactorySafe() { try { PlayerControllerB val = StartOfRound.Instance?.localPlayerController; return (Object)(object)val != (Object)null && val.isInsideFactory; } catch { return false; } } private static Type FindTypeByFullName(string fullName) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = null; try { type = assembly.GetType(fullName, throwOnError: false); } catch { } if (type != null) { return type; } } } catch { } return null; } private static Type FindTypeFuzzy(string asmNameContains, string[] typeNameCandidates) { try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string text = assembly.GetName().Name ?? ""; if (text.IndexOf(asmNameContains, StringComparison.OrdinalIgnoreCase) < 0) { continue; } Type[] source; try { source = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { source = ex.Types.Where((Type t) => t != null).ToArray(); } foreach (string cand in typeNameCandidates) { Type type = source.FirstOrDefault((Type t) => string.Equals(t.Name, cand, StringComparison.OrdinalIgnoreCase)); if (type != null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[reflection] нашёл тип " + type.FullName + " в " + text)); } return type; } } } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("FindTypeFuzzy fail: " + ex2.Message)); } } return null; } private static object ReadStaticMember(Type t, string name) { try { FieldInfo field = t.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { if (field.IsStatic) { return field.GetValue(null); } object singletonInstance = GetSingletonInstance(t); if (singletonInstance != null) { return field.GetValue(singletonInstance); } } PropertyInfo property = t.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { MethodInfo? getMethod = property.GetGetMethod(nonPublic: true); if ((object)getMethod != null && getMethod.IsStatic) { return property.GetValue(null); } object singletonInstance2 = GetSingletonInstance(t); if (singletonInstance2 != null) { return property.GetValue(singletonInstance2); } } } catch { } return null; } private static object GetSingletonInstance(Type t) { try { PropertyInfo propertyInfo = t.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public) ?? t.GetProperty("instance", BindingFlags.Static | BindingFlags.Public); if (propertyInfo != null) { return propertyInfo.GetValue(null); } FieldInfo fieldInfo = t.GetField("Instance", BindingFlags.Static | BindingFlags.Public) ?? t.GetField("instance", BindingFlags.Static | BindingFlags.Public); if (fieldInfo != null) { return fieldInfo.GetValue(null); } } catch { } return null; } private static string ExtractName(object val) { if (val == null) { return null; } try { if (val is string result) { return result; } if (val.GetType().IsEnum) { return val.ToString(); } Type type = val.GetType(); PropertyInfo propertyInfo = type.GetProperty("Name") ?? type.GetProperty("name"); if (propertyInfo != null) { string text = propertyInfo.GetValue(val) as string; if (!string.IsNullOrEmpty(text)) { return text; } } FieldInfo fieldInfo = type.GetField("Name") ?? type.GetField("name"); if (fieldInfo != null) { string text2 = fieldInfo.GetValue(val) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } string text3 = val.ToString(); if (!string.IsNullOrEmpty(text3) && text3 != type.FullName && text3 != type.Name) { return text3; } } catch { } return null; } } [BepInPlugin("gdlp.lcbridge", "LCBridge", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string GUID = "gdlp.lcbridge"; public const string NAME = "LCBridge"; public const string VERSION = "1.1.0"; private Harmony _harmony; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; int value = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "WebSocketPort", 8181, "Порт WebSocket-сервера моста. Оверлей должен слушать этот же порт.").Value; Log.LogInfo((object)"LCBridge v1.1.0 запускается..."); _harmony = new Harmony("gdlp.lcbridge"); _harmony.PatchAll(typeof(PlayerControllerB_Patches)); _harmony.PatchAll(typeof(StartOfRound_Patches)); Log.LogInfo((object)"Harmony-патчи применены."); try { BridgeServer.Start(value); Log.LogInfo((object)$"WebSocket-сервер моста запущен на ws://localhost:{value}"); } catch (Exception arg) { Log.LogError((object)$"Не удалось запустить сервер: {arg}"); } ((Component)this).gameObject.AddComponent<BridgeTicker>(); Log.LogInfo((object)"LCBridge готов."); } private void OnDestroy() { try { BridgeServer.Stop(); } catch { } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public static class RunStats { public class QuotaSlice { public int index; public int scrapStart; public int scrapEnd; public int seconds; public int deaths; } private class MoonStat { public int visits; public int profit; public int seconds; } private static readonly List<QuotaSlice> _quotas = new List<QuotaSlice>(); private static QuotaSlice _curQuota; private static int _lastQuotaIndex = -1; private static readonly Dictionary<string, MoonStat> _moons = new Dictionary<string, MoonStat>(); private static string _curMoon; private static int _moonScrapStart; private static readonly Dictionary<string, int> _monsterTime = new Dictionary<string, int>(); private static readonly Dictionary<string, int> _monsterCount = new Dictionary<string, int>(); private static readonly HashSet<int> _seenMonsterIds = new HashSet<int>(); private static int _peakMonsters; private static readonly Dictionary<int, int> _collectedScrap = new Dictionary<int, int>(); private static int _secInside; private static int _secOutside; private static readonly List<string> _timeline = new List<string>(); private static int _lastDayLogged = -1; private static string _lastEventLogged; private static int _runSeconds; public static void ResetRun() { _quotas.Clear(); _curQuota = null; _lastQuotaIndex = -1; _moons.Clear(); _curMoon = null; _moonScrapStart = 0; _monsterTime.Clear(); _peakMonsters = 0; _monsterCount.Clear(); _seenMonsterIds.Clear(); _collectedScrap.Clear(); _secInside = 0; _secOutside = 0; _timeline.Clear(); _lastDayLogged = -1; _lastEventLogged = null; _runSeconds = 0; } public static void OnDeath(string killer) { try { int dayCount = GameState.GetDayCount(); string moonName = GameState.GetMoonName(); string arg = (string.IsNullOrEmpty(killer) ? "?" : killer); _timeline.Add($"{dayCount}|death|{arg}@{moonName}"); if (_curQuota != null) { _curQuota.deaths++; } } catch { } } public static void Tick() { try { if (!GameState.GetInGame()) { return; } _runSeconds++; int dayCount = GameState.GetDayCount(); int quotaIndexSafe = GameState.GetQuotaIndexSafe(); bool onMoon = GameState.GetOnMoon(); string moonName = GameState.GetMoonName(); string brutalEvent = GameState.GetBrutalEvent(); foreach (KeyValuePair<int, int> shipScrapItem in GameState.GetShipScrapItems()) { if (!_collectedScrap.ContainsKey(shipScrapItem.Key)) { _collectedScrap[shipScrapItem.Key] = shipScrapItem.Value; } } int num = 0; foreach (int value in _collectedScrap.Values) { num += value; } if (quotaIndexSafe != _lastQuotaIndex) { if (_curQuota != null) { _curQuota.scrapEnd = num; _quotas.Add(_curQuota); } _curQuota = new QuotaSlice { index = quotaIndexSafe, scrapStart = num, scrapEnd = num, seconds = 0, deaths = 0 }; _lastQuotaIndex = quotaIndexSafe; } if (_curQuota != null) { _curQuota.seconds++; _curQuota.scrapEnd = num; } if (onMoon && !string.IsNullOrEmpty(moonName)) { if (_curMoon != moonName) { _curMoon = moonName; _moonScrapStart = num; if (!_moons.ContainsKey(moonName)) { _moons[moonName] = new MoonStat(); } _moons[moonName].visits++; } MoonStat moonStat = _moons[moonName]; moonStat.seconds++; int num2 = num - _moonScrapStart; if (num2 > 0) { moonStat.profit += num2; _moonScrapStart = num; } } else { _curMoon = null; } List<KeyValuePair<int, string>> monsterInstances = GameState.GetMonsterInstances(); foreach (KeyValuePair<int, string> item in monsterInstances) { Add(_monsterTime, item.Value); if (_seenMonsterIds.Add(item.Key)) { Add(_monsterCount, item.Value); } } int count = monsterInstances.Count; if (count > _peakMonsters) { _peakMonsters = count; } if (onMoon) { if (GameState.GetInsideFactorySafe()) { _secInside++; } else { _secOutside++; } } if (dayCount != _lastDayLogged && dayCount > 0) { _lastDayLogged = dayCount; _timeline.Add($"{dayCount}|day|{moonName}"); } if (!string.IsNullOrEmpty(brutalEvent) && brutalEvent != "—" && brutalEvent != _lastEventLogged) { _lastEventLogged = brutalEvent; _timeline.Add($"{dayCount}|event|{brutalEvent}"); } } catch { } } private static void Add(Dictionary<string, int> d, string k) { if (!string.IsNullOrEmpty(k)) { d.TryGetValue(k, out var value); d[k] = value + 1; } } public static string ToJson() { try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('{'); stringBuilder.Append("\"quotas\":["); List<QuotaSlice> list = new List<QuotaSlice>(_quotas); if (_curQuota != null) { list.Add(_curQuota); } for (int i = 0; i < list.Count; i++) { QuotaSlice quotaSlice = list[i]; if (i > 0) { stringBuilder.Append(','); } int value = Math.Max(0, quotaSlice.scrapEnd - quotaSlice.scrapStart); stringBuilder.Append('{').Append("\"i\":").Append(quotaSlice.index) .Append(',') .Append("\"money\":") .Append(value) .Append(',') .Append("\"sec\":") .Append(quotaSlice.seconds) .Append(',') .Append("\"deaths\":") .Append(quotaSlice.deaths) .Append('}'); } stringBuilder.Append("],"); stringBuilder.Append("\"moons\":["); int num = 0; foreach (KeyValuePair<string, MoonStat> item in _moons.OrderByDescending((KeyValuePair<string, MoonStat> x) => x.Value.profit)) { if (num++ > 0) { stringBuilder.Append(','); } stringBuilder.Append('{').Append("\"name\":").Append(JsonStr(item.Key)) .Append(',') .Append("\"visits\":") .Append(item.Value.visits) .Append(',') .Append("\"profit\":") .Append(item.Value.profit) .Append(',') .Append("\"sec\":") .Append(item.Value.seconds) .Append('}'); } stringBuilder.Append("],"); stringBuilder.Append("\"monsters\":["); int num2 = 0; foreach (KeyValuePair<string, int> item2 in _monsterCount.OrderByDescending((KeyValuePair<string, int> x) => x.Value).Take(20)) { if (num2++ > 0) { stringBuilder.Append(','); } _monsterTime.TryGetValue(item2.Key, out var value2); stringBuilder.Append('{').Append("\"name\":").Append(JsonStr(item2.Key)) .Append(',') .Append("\"count\":") .Append(item2.Value) .Append(',') .Append("\"sec\":") .Append(value2) .Append('}'); } stringBuilder.Append("],"); stringBuilder.Append("\"peak\":").Append(_peakMonsters).Append(','); stringBuilder.Append("\"inside\":").Append(_secInside).Append(','); stringBuilder.Append("\"outside\":").Append(_secOutside).Append(','); stringBuilder.Append("\"runSec\":").Append(_runSeconds).Append(','); stringBuilder.Append("\"timeline\":["); for (int num3 = 0; num3 < _timeline.Count && num3 < 60; num3++) { if (num3 > 0) { stringBuilder.Append(','); } stringBuilder.Append(JsonStr(_timeline[num3])); } stringBuilder.Append(']'); stringBuilder.Append('}'); return stringBuilder.ToString(); } catch { return "{}"; } } private static string JsonStr(string s) { if (s == null) { return "\"\""; } StringBuilder stringBuilder = new StringBuilder("\""); foreach (char c in s) { switch (c) { case '"': case '\\': stringBuilder.Append('\\').Append(c); break; case '\n': case '\r': stringBuilder.Append(' '); break; default: stringBuilder.Append(c); break; } } stringBuilder.Append('"'); return stringBuilder.ToString(); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "LCBridge"; public const string PLUGIN_NAME = "LCBridge"; public const string PLUGIN_VERSION = "1.1.0"; } } namespace LCBridge.Patches { [HarmonyPatch(typeof(PlayerControllerB))] public static class PlayerControllerB_Patches { [HarmonyPatch("KillPlayer")] [HarmonyPostfix] public static void OnKillPlayer(PlayerControllerB __instance, CauseOfDeath causeOfDeath) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)null && __instance.isPlayerDead) { string killer = ResolveKiller(__instance, causeOfDeath); GameState.RegisterDeath(__instance, killer); } } private static string ResolveKiller(PlayerControllerB player, CauseOfDeath cause) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected I4, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) try { if ((int)cause != 2) { switch (cause - 5) { case 4: return "Утопление"; case 0: return "Удушье"; case 8: return "Огонь"; case 6: return "Ток"; case 3: return "Раздавлен"; default: { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null || instance.SpawnedEnemies == null) { return "Неизвестно"; } Vector3 position = ((Component)player).transform.position; float num = 25f; string text = null; foreach (EnemyAI spawnedEnemy in instance.SpawnedEnemies) { if (!((Object)(object)spawnedEnemy == (Object)null) && !spawnedEnemy.isEnemyDead) { float num2 = Vector3.Distance(position, ((Component)spawnedEnemy).transform.position); if (num2 < num) { num = num2; text = EnemyResolver.Resolve(spawnedEnemy); } } } return string.IsNullOrEmpty(text) ? "Неизвестно" : text; } } } return "Падение"; } catch { return "Неизвестно"; } } } [HarmonyPatch(typeof(StartOfRound))] public static class StartOfRound_Patches { [HarmonyPatch("ResetShip")] [HarmonyPostfix] public static void OnResetShip() { GameState.ResetDeaths(); } [HarmonyPatch("SetTimeAndPlanetToSavedSettings")] [HarmonyPostfix] public static void OnLoadSavedSettings() { try { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && ((instance.gameStats != null && instance.gameStats.daysSpent <= 0) || ((Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.timesFulfilledQuota <= 0 && TimeOfDay.Instance.daysUntilDeadline >= 3))) { GameState.ResetDeaths(); } } catch { } } [HarmonyPatch("StartGame")] [HarmonyPostfix] public static void OnStartGame() { GameState.OnNewRound(); } } }