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