Decompiled source of LethalLetdownBot v1.1.0

packsolite.lethalletdownbot.dll

Decompiled 3 weeks 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.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()}");
		}
	}
}