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()}");
}
}
}