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 LethalLetdownBot v1.1.0
packsolite.lethalletdownbot.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Discord; using HarmonyLib; using LethalConfig; using LethalConfig.ConfigItems; using LethalLetdownBot.Client; using LethalLetdownBot.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Steamworks; using Steamworks.Data; using Unity.Netcode; 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("packsolite.lethalletdownbot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+bb9a837a2900004f9e5c17e2214be18e77af65d7")] [assembly: AssemblyProduct("LethalLetdownBot")] [assembly: AssemblyTitle("packsolite.lethalletdownbot")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 LethalLetdownBot { [BepInPlugin("packsolite.lethalletdownbot", "LethalLetdownBot", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class LethalLetdownBot : BaseUnityPlugin { public static LethalLetdownBot Instance { get; private set; } = null; internal static ManualLogSource Logger { get; private set; } = null; internal static Harmony Harmony { get; set; } = null; internal static string Token { get; set; } = null; internal static string URL { get; set; } = null; internal static bool Enabled { get; set; } = true; private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; Instance = this; LoadConfig(); Token = UserUtil.GetOrCreateUserId(); Patch(); Logger.LogInfo((object)"packsolite.lethalletdownbot v1.1.0 has loaded!"); } internal static void Patch() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony("packsolite.lethalletdownbot"); } Harmony.PatchAll(); } internal static void Unpatch() { Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void LoadConfig() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown ConfigEntry<bool> enableEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable bot", true, "Controls wether the bot announces the lobby on discord. Can be used to quickly toggle the bot while the game is running."); BoolCheckBoxConfigItem val = new BoolCheckBoxConfigItem(enableEntry, false); ConfigEntry<string> endpointEntry = ((BaseUnityPlugin)this).Config.Bind<string>("Configuration", "Endpoint", "http://localhost:8080/", "Endpoint to use for the LethalLetdown-Bot"); TextInputFieldConfigItem val2 = new TextInputFieldConfigItem(endpointEntry, false); endpointEntry.SettingChanged += ApplyConfig; enableEntry.SettingChanged += ApplyConfig; LethalConfigManager.AddConfigItem((BaseConfigItem)(object)val2); LethalConfigManager.AddConfigItem((BaseConfigItem)(object)val); ApplyConfig(null, null); void ApplyConfig(object? obj, EventArgs? args) { URL = endpointEntry.Value.TrimEnd('/') + "/"; Enabled = enableEntry.Value; if (Enabled) { LobbyManager.Instance.CreateLobby(); } else { LobbyManager.Instance.DeleteLobby(); } } } } public class LobbyManager { private static readonly Lazy<LobbyManager> _instance = new Lazy<LobbyManager>(() => new LobbyManager()); public static LobbyManager Instance => _instance.Value; private LobbyManager() { } public void CreateLobby() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if (instance == null || !((NetworkBehaviour)instance).IsHost) { return; } string lobbyName = GameNetworkManager.Instance.lobbyHostSettings.lobbyName; GameNetworkManager instance2 = GameNetworkManager.Instance; object obj; if (instance2 == null) { obj = null; } else { Lobby? currentLobby = instance2.currentLobby; if (!currentLobby.HasValue) { obj = null; } else { Lobby valueOrDefault = currentLobby.GetValueOrDefault(); SteamId id = ((Lobby)(ref valueOrDefault)).Id; obj = ((object)(SteamId)(ref id)).ToString(); } } if (obj == null) { obj = string.Empty; } string steamLobbyId = (string)obj; bool isPublic = GameNetworkManager.Instance?.lobbyHostSettings.isLobbyPublic ?? false; LobbyStatus currentLobbyStatus = GetCurrentLobbyStatus(); LobbyOwner currentLobbyOwner = GetCurrentLobbyOwner(); RestClient.Instance.CreateLobby(currentLobbyOwner, currentLobbyStatus, lobbyName, steamLobbyId, isPublic).ContinueWith(delegate(Task t) { if (t.IsFaulted) { LogHelper.Error(t.Exception?.GetBaseException(), "Failed creating lobby"); } }); } public void UpdateLobby() { LobbyStatus status = GetCurrentLobbyStatus(); RestClient.Instance.UpdateLobbyStatus(status).ContinueWith(delegate(Task t) { LogHelper.LogFaulted(t, $"Failed updating lobby status {status}"); }); } public void DeleteLobby() { RestClient.Instance.DestroyLobby().ContinueWith(delegate(Task t) { LogHelper.LogFaulted(t, "Failed deleting lobby"); }); } private static LobbyStatus GetCurrentLobbyStatus() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; SelectableLevel currentLevel = instance.currentLevel; CurrentMoon currentMoon = (((Object)(object)currentLevel != (Object)null) ? CurrentMoon.FromSelectableLevel(currentLevel) : null); int connectedPlayers = GameNetworkManager.Instance.connectedPlayers; GameNetworkManager instance2 = GameNetworkManager.Instance; int? obj; if (instance2 == null) { obj = null; } else { Lobby? currentLobby = instance2.currentLobby; if (!currentLobby.HasValue) { obj = null; } else { Lobby valueOrDefault = currentLobby.GetValueOrDefault(); obj = ((Lobby)(ref valueOrDefault)).MaxMembers; } } int? num = obj; int valueOrDefault2 = num.GetValueOrDefault(4); ShipStatus shipStatus = (instance.firingPlayersCutsceneRunning ? ShipStatus.Fired : (instance.shipIsLeaving ? ShipStatus.Leaving : (instance.beganLoadingNewLevel ? ShipStatus.Landing : ((!instance.inShipPhase) ? ((!instance.currentLevel.planetHasTime && instance.currentLevel.riskLevel.Equals("Safe")) ? ShipStatus.Selling : ShipStatus.Landed) : ShipStatus.Orbiting)))); return new LobbyStatus(shipStatus, currentMoon, connectedPlayers, valueOrDefault2); } private static LobbyOwner GetCurrentLobbyOwner() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) string name = GameNetworkManager.Instance.localPlayerController?.playerUsername ?? "unknown"; string id = GameNetworkManager.Instance.localPlayerController?.playerSteamId.ToString() ?? string.Empty; DiscordController instance = DiscordController.Instance; long? obj; if (instance == null) { obj = null; } else { Discord discord = instance.discord; if (discord == null) { obj = null; } else { UserManager userManager = discord.GetUserManager(); obj = ((userManager != null) ? new long?(userManager.GetCurrentUser().Id) : null); } } long? num = obj; long valueOrDefault = num.GetValueOrDefault(); return new LobbyOwner(name, id, valueOrDefault); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "packsolite.lethalletdownbot"; public const string PLUGIN_NAME = "LethalLetdownBot"; public const string PLUGIN_VERSION = "1.1.0"; } } namespace LethalLetdownBot.Utils { internal static class LogHelper { public static void Info(string message) { LethalLetdownBot.Logger.LogInfo((object)message); } public static void Warn(string message) { LethalLetdownBot.Logger.LogWarning((object)message); } public static void Error(string message) { LethalLetdownBot.Logger.LogError((object)message); } public static void Error(Exception ex, string context = "Unhandled exception") { string text = ex?.GetBaseException().Message ?? "Unknown error"; LethalLetdownBot.Logger.LogError((object)(context + ": " + text)); } public static void LogFaulted(Task task, string context) { if (task.IsFaulted) { string text = task.Exception?.GetBaseException().Message ?? "Unknown error"; LethalLetdownBot.Logger.LogError((object)(context + ": " + text)); } } } public static class UserUtil { private static readonly string FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "LethalLetdown", "user-secret.txt"); public static string GetOrCreateUserId() { if (File.Exists(FilePath)) { return File.ReadAllText(FilePath).Trim(); } string text = Guid.NewGuid().ToString(); Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); File.WriteAllText(FilePath, text); return text; } } } namespace LethalLetdownBot.Patches { [HarmonyPatch(typeof(StartOfRound))] public class StartOfRoundPatch { [CompilerGenerated] private sealed class <CreateLobbyDelayed>d__10 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CreateLobbyDelayed>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } LobbyManager.Instance.CreateLobby(); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPatch("Start")] [HarmonyPrefix] private static void OnCreateLobby(StartOfRound __instance) { if (((NetworkBehaviour)__instance).IsHost && LethalLetdownBot.Enabled) { ((MonoBehaviour)__instance).StartCoroutine(CreateLobbyDelayed(__instance)); } } [HarmonyPatch("OnLocalDisconnect")] [HarmonyPrefix] private static void OnDestroyLobby(StartOfRound __instance) { if (((NetworkBehaviour)__instance).IsHost && LethalLetdownBot.Enabled) { LobbyManager.Instance.DeleteLobby(); } } [HarmonyPatch("StartGame")] [HarmonyPostfix] private static void OnStartGame(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("OnShipLandedMiscEvents")] [HarmonyPostfix] private static void OnShipLanded(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("ShipLeave")] [HarmonyPostfix] private static void OnShipLeave(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("SetShipReadyToLand")] [HarmonyPostfix] private static void OnUpdateDiscordStatus(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("FirePlayersAfterDeadlineClientRpc")] [HarmonyPostfix] private static void OnPlayersFired(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("OnClientConnect")] [HarmonyPostfix] private static void OnClientConnected(StartOfRound __instance) { UpdateStatus(__instance); } [HarmonyPatch("OnClientDisconnect")] [HarmonyPostfix] private static void OnClientDisconnected(StartOfRound __instance) { UpdateStatus(__instance); } private static void UpdateStatus(StartOfRound startOfRound) { LobbyManager.Instance.UpdateLobby(); } [IteratorStateMachine(typeof(<CreateLobbyDelayed>d__10))] private static IEnumerator CreateLobbyDelayed(StartOfRound __instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CreateLobbyDelayed>d__10(0); } } } namespace LethalLetdownBot.Client { public enum ShipStatus { Orbiting, Landing, Landed, Leaving, Selling, Fired } public class CurrentMoon { public string Name { get; set; } public string PlanetName { get; set; } public string CurrentWeather { get; set; } public string RiskLevel { get; set; } public CurrentMoon() { } public CurrentMoon(string name, string planetName, string currentWeather, string riskLevel) { Name = name; PlanetName = planetName; CurrentWeather = currentWeather; RiskLevel = riskLevel; } public static CurrentMoon FromSelectableLevel(SelectableLevel level) { return new CurrentMoon(((Object)level).name, level.PlanetName, ((object)(LevelWeatherType)(ref level.currentWeather)).ToString(), level.riskLevel); } } public class LobbyStatus { public ShipStatus ShipStatus { get; set; } public CurrentMoon? CurrentMoon { get; set; } public int PlayerCount { get; set; } public int MaxPlayerCount { get; set; } public LobbyStatus() { } public LobbyStatus(ShipStatus shipStatus, CurrentMoon currentMoon, int playerCount, int maxPlayerCount) { ShipStatus = shipStatus; CurrentMoon = currentMoon; PlayerCount = playerCount; MaxPlayerCount = maxPlayerCount; } } public class LobbyOwner { public string Name { get; set; } public string ID { get; set; } public long DiscordId { get; set; } public LobbyOwner() { } public LobbyOwner(string name, string id, long discordId) { Name = name; ID = id; DiscordId = discordId; } } public class Lobby { public LobbyStatus Status { get; set; } public LobbyOwner Owner { get; set; } } internal class LobbyStatusUpdateRequest { public LobbyStatus Status { get; set; } } internal class LobbyCreateRequest { public string Token { get; set; } public string Name { get; set; } public string SteamLobby { get; set; } public LobbyOwner Owner { get; set; } public LobbyStatus Status { get; set; } public bool IsPublic { get; set; } } internal class LobbyCreateResponse { public bool Successful { get; set; } public string LobbyId { get; set; } } public class RestClient { private static readonly Lazy<RestClient> _instance = new Lazy<RestClient>(() => new RestClient()); private readonly HttpClient _httpClient = new HttpClient(); private string _lobbyId; public static RestClient Instance => _instance.Value; private RestClient() { StartHeartbeatTimer(); } private void StartHeartbeatTimer() { Timer timer = new Timer(delegate { SendHeartbeat().ContinueWith(delegate(Task t) { LogHelper.LogFaulted(t, "Failed sending heartbeat"); }); }, null, TimeSpan.Zero, TimeSpan.FromMinutes(1.0)); } public async Task SendHeartbeat() { if (_lobbyId != null) { await _httpClient.PostAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId + "/heartbeat", new ByteArrayContent(new byte[0])); } } public async Task CreateLobby(LobbyOwner owner, LobbyStatus status, string lobbyName, string steamLobbyId, bool isPublic) { LobbyCreateRequest lobbyCreateRequest = new LobbyCreateRequest { Token = LethalLetdownBot.Token, Name = lobbyName, SteamLobby = steamLobbyId, Owner = owner, Status = status, IsPublic = isPublic }; StringContent content = new StringContent(JsonConvert.SerializeObject((object)lobbyCreateRequest), Encoding.UTF8, "application/json"); HttpResponseMessage httpResponseMessage = await _httpClient.PostAsync(LethalLetdownBot.URL + "lobby", content); if (!httpResponseMessage.IsSuccessStatusCode) { object arg = httpResponseMessage.StatusCode; LogHelper.Error($"Failed to post: {arg} - {await httpResponseMessage.Content.ReadAsStringAsync()}"); return; } string text = await httpResponseMessage.Content.ReadAsStringAsync(); try { LobbyCreateResponse lobbyCreateResponse = JsonConvert.DeserializeObject<LobbyCreateResponse>(text); if (lobbyCreateResponse == null || !lobbyCreateResponse.Successful) { LogHelper.Error("Failed to create lobby: " + (lobbyCreateResponse?.LobbyId ?? ("invalid response: " + text))); return; } _lobbyId = lobbyCreateResponse.LobbyId; LethalLetdownBot.Logger.LogInfo((object)("Successfully created lobby with id '" + _lobbyId + "'")); } catch (Exception ex) { LogHelper.Error(ex, "Failed deserializing response"); } } public async Task DestroyLobby() { if (_lobbyId == null) { LogHelper.Error("Could not delete lobby: _lobbyId is null"); return; } await _httpClient.DeleteAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId); LethalLetdownBot.Logger.LogInfo((object)("Successfully deleted lobby '" + _lobbyId + "'")); _lobbyId = null; } public async Task UpdateLobbyStatus(LobbyStatus status) { if (_lobbyId == null) { LogHelper.Error("Could not update lobby status: _lobbyId is null"); return; } StringContent content = new StringContent(JsonConvert.SerializeObject((object)new LobbyStatusUpdateRequest { Status = status }), Encoding.UTF8, "application/json"); HttpResponseMessage httpResponseMessage = await _httpClient.PutAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId + "/status", content); if (httpResponseMessage.IsSuccessStatusCode) { LogHelper.Info($"Successfully updated lobby (status={status.ShipStatus}, players={status.PlayerCount})"); return; } object arg = httpResponseMessage.StatusCode; LogHelper.Error($"Failed to post: {arg} - {await httpResponseMessage.Content.ReadAsStringAsync()}"); } } }