Decompiled source of LethalLetdownBot v1.0.6

packsolite.lethalletdownbot.dll

Decompiled 3 months ago
using 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("packsolite.lethalletdownbot")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyProduct("LethalLetdownBot")]
[assembly: AssemblyTitle("packsolite.lethalletdownbot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.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.0.6")]
	[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.0.6 has loaded!");
		}

		internal static void Patch()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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());

		private bool _lobbyCreated;

		public static LobbyManager Instance => _instance.Value;

		private LobbyManager()
		{
		}

		public void CreateLobby()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if (instance == null || !((NetworkBehaviour)instance).IsHost)
			{
				return;
			}
			if (_lobbyCreated)
			{
				DeleteLobby();
			}
			string lobbyName = GameNetworkManager.Instance.lobbyHostSettings.lobbyName;
			GameNetworkManager instance2 = GameNetworkManager.Instance;
			SteamId? 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)).Id;
				}
			}
			SteamId? val = obj;
			string steamLobbyId = val.ToString();
			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("Failed creating lobby: " + t.Exception?.GetBaseException().Message);
				}
				else
				{
					_lobbyCreated = true;
				}
			});
		}

		public void UpdateLobby()
		{
			if (_lobbyCreated)
			{
				LobbyStatus status = GetCurrentLobbyStatus();
				RestClient.Instance.UpdateLobbyStatus(status).ContinueWith(delegate(Task t)
				{
					LogHelper.LogFaulted(t, "Failed updating lobby status " + status.ToString());
				});
			}
		}

		public void DeleteLobby()
		{
			if (_lobbyCreated)
			{
				RestClient.Instance.DestroyLobby().ContinueWith(delegate(Task t)
				{
					LogHelper.LogFaulted(t, "Failed deleting lobby");
				});
				_lobbyCreated = false;
			}
		}

		private static LobbyStatus GetCurrentLobbyStatus()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			SelectableLevel currentLevel = instance.currentLevel;
			if ((Object)(object)currentLevel == (Object)null)
			{
				return new LobbyStatus(ShipStatus.Orbiting, null, 1, -1);
			}
			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(-1);
			ShipStatus shipStatus = (instance.firingPlayersCutsceneRunning ? ShipStatus.Fired : ((!instance.inShipPhase) ? (instance.shipIsLeaving ? ShipStatus.Leaving : ((!instance.currentLevel.planetHasTime && instance.currentLevel.riskLevel.Equals("Safe")) ? ShipStatus.Selling : ((!instance.shipDoorsEnabled) ? ShipStatus.Landing : ShipStatus.Landed))) : ShipStatus.Orbiting));
			return new LobbyStatus(shipStatus, CurrentMoon.OfSelectableLevel(currentLevel), connectedPlayers, valueOrDefault2);
		}

		private static LobbyOwner GetCurrentLobbyOwner()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			string name = GameNetworkManager.Instance.localPlayerController?.playerUsername ?? "unknown";
			string steamId = (GameNetworkManager.Instance.localPlayerController?.playerSteamId).ToString();
			DiscordController instance = DiscordController.Instance;
			User? 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 User?(userManager.GetCurrentUser()) : null);
				}
			}
			User? val = obj;
			long? discordId = val?.Id;
			return new LobbyOwner(name, steamId, discordId);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "packsolite.lethalletdownbot";

		public const string PLUGIN_NAME = "LethalLetdownBot";

		public const string PLUGIN_VERSION = "1.0.6";
	}
}
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__7 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public StartOfRound __instance;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CreateLobbyDelayed>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: 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("SetDiscordStatusDetails")]
		[HarmonyPostfix]
		private static void OnUpdateDiscordStatus(StartOfRound __instance)
		{
			UpdateStatus(__instance);
		}

		[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);
		}

		private static void UpdateStatus(StartOfRound startOfRound)
		{
			LobbyManager.Instance.UpdateLobby();
		}

		[IteratorStateMachine(typeof(<CreateLobbyDelayed>d__7))]
		private static IEnumerator CreateLobbyDelayed(StartOfRound __instance)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CreateLobbyDelayed>d__7(0)
			{
				__instance = __instance
			};
		}
	}
}
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(string name, string planetName, string currentWeather, string riskLevel)
		{
			Name = name;
			PlanetName = planetName;
			CurrentWeather = currentWeather;
			RiskLevel = riskLevel;
			base..ctor();
		}

		public static CurrentMoon OfSelectableLevel(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(ShipStatus shipStatus, CurrentMoon? currentMoon, int? players, int? maxPlayers)
		{
			ShipStatus = shipStatus;
			CurrentMoon = currentMoon;
			PlayerCount = players;
			MaxPlayerCount = maxPlayers;
			base..ctor();
		}
	}
	public class LobbyOwner
	{
		public string ID { get; set; }

		public string Name { get; set; }

		public long? DiscordId { get; set; }

		public LobbyOwner(string name, string steamId, long? discordId)
		{
			ID = steamId;
			Name = name;
			DiscordId = discordId;
			base..ctor();
		}
	}
	public class Lobby
	{
		public LobbyStatus? Status { get; set; }

		public LobbyOwner Owner { get; set; }

		public Lobby(LobbyOwner owner)
		{
			Owner = owner;
			base..ctor();
		}
	}
	internal class LobbyStatusUpdateRequest
	{
		public LobbyStatus Status { get; set; }

		public LobbyStatusUpdateRequest(LobbyStatus status)
		{
			Status = status;
			base..ctor();
		}
	}
	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; }

		public LobbyCreateRequest(LobbyOwner owner, LobbyStatus status, string authToken, string lobbyName, string steamLobbyId, bool isPublic)
		{
			Token = authToken;
			Name = lobbyName;
			SteamLobby = steamLobbyId;
			Owner = owner;
			Status = status;
			IsPublic = isPublic;
			base..ctor();
		}
	}
	internal class LobbyCreateResponse
	{
		public bool Successful { get; set; }

		public string LobbyId { get; set; }

		public LobbyCreateResponse(bool successful, string lobbyId)
		{
			Successful = successful;
			LobbyId = lobbyId;
			base..ctor();
		}
	}
	public class RestClient
	{
		private static readonly Lazy<RestClient> _instance = new Lazy<RestClient>(() => new RestClient());

		private readonly HttpClient _httpClient;

		private string? _lobbyId;

		public static RestClient Instance => _instance.Value;

		public RestClient()
		{
			_httpClient = new HttpClient();
			StartHeatbeatTimer();
		}

		private void StartHeatbeatTimer()
		{
			TimeSpan zero = TimeSpan.Zero;
			TimeSpan period = TimeSpan.FromMinutes(2.0);
			Timer timer = new Timer(delegate
			{
				SendHeartbeat().ContinueWith(delegate(Task t)
				{
					LogHelper.LogFaulted(t, "Failed sending heardbeat");
				});
			}, null, zero, period);
		}

		public async Task SendHeartbeat()
		{
			if (_lobbyId != null)
			{
				ByteArrayContent content = new ByteArrayContent(new byte[0]);
				await _httpClient.PostAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId + "/heartbeat", content);
			}
		}

		public async Task CreateLobby(LobbyOwner owner, LobbyStatus status, string lobbyName, string steamLobbyId, bool isPublic)
		{
			LobbyCreateRequest request = new LobbyCreateRequest(owner, status, LethalLetdownBot.Token, lobbyName, steamLobbyId, isPublic);
			string json = JsonConvert.SerializeObject((object)request);
			StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
			HttpResponseMessage response = await _httpClient.PostAsync(LethalLetdownBot.URL + "lobby", content);
			if (!response.IsSuccessStatusCode)
			{
				object arg = response.StatusCode;
				LogHelper.Error($"Failed to post: {arg} - {await response.Content.ReadAsStringAsync()}");
				return;
			}
			string responseContent = await response.Content.ReadAsStringAsync();
			try
			{
				LobbyCreateResponse result = JsonConvert.DeserializeObject<LobbyCreateResponse>(responseContent);
				if (result == null)
				{
					LogHelper.Error("Failed to create lobby: invalid response from endpoint: " + responseContent);
					return;
				}
				if (!result.Successful)
				{
					LogHelper.Error("Failed to create lobby: " + result.LobbyId);
					return;
				}
				_lobbyId = result.LobbyId;
				LethalLetdownBot.Logger.LogInfo((object)("Successfully created lobby with id '" + _lobbyId + "'"));
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "Failed deserializing response: " + responseContent);
			}
		}

		public async Task DestroyLobby()
		{
			if (_lobbyId == null)
			{
				LogHelper.Error("Could not delete lobby: _lobbyId is null");
				return;
			}
			await _httpClient.DeleteAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId);
			LogHelper.Info("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;
			}
			LobbyStatusUpdateRequest request = new LobbyStatusUpdateRequest(status);
			string json = JsonConvert.SerializeObject((object)request);
			StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
			HttpResponseMessage response = await _httpClient.PutAsync(LethalLetdownBot.URL + "lobby/" + _lobbyId + "/status", content);
			if (response.IsSuccessStatusCode)
			{
				LogHelper.Info($"Successfully updated lobby (status={status.ShipStatus}, players={status.PlayerCount})");
				return;
			}
			object arg = response.StatusCode;
			LogHelper.Error($"Failed to post: {arg} - {await response.Content.ReadAsStringAsync()}");
		}
	}
}