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 DevLaunch v0.1.1
plugins/tech.rethunk.mortar.DevLaunch.dll
Decompiled 21 hours agousing System; using System.Collections; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ES3Internal; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using Steamworks; using Unity.Netcode; using Unity.Netcode.Transports.UTP; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [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("Rethunk Tech")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 Rethunk Tech")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1+2f64a2e84d6c051e6cfb058b6084defc73e778bf")] [assembly: AssemblyProduct("tech.rethunk.mortar.DevLaunch")] [assembly: AssemblyTitle("DevLaunch")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Rethunk-Tech/mortar-lc-devlaunch")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 DevLaunch { internal enum Corner { TopLeft, TopRight, BottomLeft, BottomRight } internal sealed class Badge : MonoBehaviour { private static ConfigEntry<bool> inTitle; private static ConfigEntry<bool> inHud; private static ConfigEntry<Corner> corner; private static ConfigEntry<string> color; private static ConfigEntry<float> size; private GUIStyle? style; private string shown = ""; private long state = long.MinValue; public static void Bind(ConfigFile config) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown inTitle = config.Bind<bool>("Label", "Show in title", true, "Put which copy this is (Host, Client 2, ...) in the window title."); inHud = config.Bind<bool>("Label", "Show in game HUD", true, "Draw which copy this is over the game."); corner = config.Bind<Corner>("Label", "Corner in HUD", Corner.TopLeft, "Which corner of the window to draw the name in."); color = config.Bind<string>("Label", "Text color", "#FFFFFF", "The colour of the name drawn over the game, as a hex colour like #FFFFFF."); size = config.Bind<float>("Label", "Text size", 1f, new ConfigDescription("The size of the name drawn over the game, relative to the window. At 1 it's a thirtieth of the window's height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 4f), Array.Empty<object>())); } public static void Show() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown GameObject val = new GameObject("DevLaunch") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<Badge>(); Tile.Add(val); } private void Awake() { inTitle.SettingChanged += delegate { state = long.MinValue; }; } private void Update() { NetworkManager singleton = NetworkManager.Singleton; long num = (((Object)(object)singleton != (Object)null && singleton.IsHost) ? (-1) : (((Object)(object)singleton != (Object)null && singleton.IsConnectedClient) ? ((long)singleton.LocalClientId) : (-2L))); if (num != state) { state = num; shown = ((num == -1) ? "Host" : ((num >= 0) ? $"Client {num + 1}" : (Plugin.Launch.Host ? "Host" : "Client"))); Plugin.Log.LogInfo((object)("This copy: " + shown)); WindowTitle.Set(inTitle.Value ? ("Lethal Company - " + shown) : "Lethal Company"); } } private static Color TextColor() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) string text = color.Value.Trim(); Color result = default(Color); if (!ColorUtility.TryParseHtmlString(text.StartsWith("#") ? text : ("#" + text), ref result)) { return Color.white; } return result; } private void OnGUI() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) if (inHud.Value) { int num = Mathf.Max(12, (int)((float)Screen.height / 30f * size.Value)); if (style == null) { style = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1 }; } style.fontSize = num; style.normal.textColor = TextColor(); Corner value = corner.Value; bool flag = ((value == Corner.TopLeft || value == Corner.BottomLeft) ? true : false); bool flag2 = flag; value = corner.Value; flag = (uint)value <= 1u; bool flag3 = flag; style.alignment = (TextAnchor)((!flag3) ? (flag2 ? 6 : 8) : ((!flag2) ? 2 : 0)); float num2 = num * 12; float num3 = num * 2; float num4 = (flag2 ? ((float)num) : ((float)(Screen.width - num) - num2)); float num5 = (flag3 ? ((float)num / 2f) : ((float)Screen.height - (float)num / 2f - num3)); GUI.Label(new Rect(num4, num5, num2, num3), shown, style); } } } internal static class Menus { private const int JoinAttempts = 10; private const int SaveAttempts = 40; private static Hook? launchModeHook; private static Hook? menuHook; private static Hook? screenModeHook; private static Hook? bootHook; private static Hook? saveHook; private static bool started; public static void Hook() { launchModeHook = Detour(typeof(PreInitSceneScript).GetMethod("SetLaunchPanelsEnabled"), new Action<Action<PreInitSceneScript>, PreInitSceneScript>(PickLaunchMode)); menuHook = Detour(typeof(MenuManager).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic), new Action<Action<MenuManager>, MenuManager>(StartGame)); screenModeHook = Detour(typeof(IngamePlayerSettings).GetMethod("SetFullscreenMode", BindingFlags.Instance | BindingFlags.NonPublic), new Action<Action<IngamePlayerSettings, int>, IngamePlayerSettings, int>(KeepTiled)); bootHook = Detour(typeof(InitializeGame).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic), new Action<Action<InitializeGame>, InitializeGame>(SkipBootUp)); saveHook = Detour(typeof(ES3Stream).GetMethod("CreateStream", new Type[2] { typeof(ES3Settings), typeof(ES3FileMode) }), new Func<Func<ES3Settings, ES3FileMode, Stream>, ES3Settings, ES3FileMode, Stream>(SharedSave)); } private static Stream SharedSave(Func<ES3Settings, ES3FileMode, Stream> orig, ES3Settings settings, ES3FileMode mode) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) int num = 1; while (true) { try { return orig(settings, mode); } catch (IOException ex) when (num < 40 && ex.Message.Contains("Sharing violation")) { Thread.Sleep(50); } num++; } } private static void SkipBootUp(Action<InitializeGame> orig, InitializeGame self) { orig(self); self.runBootUpScreen = false; self.playColdOpenCinematic = false; self.playColdOpenCinematic2 = false; } private static Hook? Detour(MethodInfo? method, Delegate to) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown if (method == null) { Plugin.Log.LogError((object)("Couldn't find the game method that " + to.Method.Name + " hooks. A game update may have changed it, so that part is off")); return null; } return new Hook((MethodBase)method, to); } private static void KeepTiled(Action<IngamePlayerSettings, int> orig, IngamePlayerSettings self, int value) { orig(self, value); Tile.Apply(); } private static void PickLaunchMode(Action<PreInitSceneScript> orig, PreInitSceneScript self) { bool? online = Plugin.Launch.Online; if (online.HasValue) { bool valueOrDefault = online == true; self.ChooseLaunchOption(valueOrDefault); } else { orig(self); } } private static void StartGame(Action<MenuManager> orig, MenuManager self) { orig(self); if (Plugin.Launch.Online == false && !self.isInitScene) { self.lanWarningContainer.SetActive(false); } if (started || self.isInitScene) { return; } started = true; if (Plugin.Launch.Host) { self.SetLoadingScreen(true, (RoomEnter)5, ""); ((MonoBehaviour)self).StartCoroutine(Host(self)); return; } string joinAddress = Plugin.Launch.JoinAddress; if (joinAddress != null) { if (!GameNetworkManager.Instance.disableSteam) { Plugin.Log.LogWarning((object)"--join needs LAN mode; online games are joined through Steam"); return; } self.SetLoadingScreen(true, (RoomEnter)5, ""); ((MonoBehaviour)self).StartCoroutine(Join(self, joinAddress)); } } private static IEnumerator Host(MenuManager menu) { yield return null; Plugin.Log.LogInfo((object)"Hosting a game"); menu.ClickHostButton(); menu.ConfirmHostButton(); } private static IEnumerator Join(MenuManager menu, string address) { ((Component)NetworkManager.Singleton).GetComponent<UnityTransport>().ConnectionData.Address = address; for (int attempt = 1; attempt <= 10; attempt++) { yield return (object)new WaitForSeconds(2f); Plugin.Log.LogInfo((object)$"Joining {address}, attempt {attempt}"); menu.menuNotification.SetActive(false); menu.StartAClient(); menu.SetLoadingScreen(true, (RoomEnter)5, ""); float giveUp = Time.realtimeSinceStartup + 12f; while (Time.realtimeSinceStartup < giveUp) { if (NetworkManager.Singleton.IsConnectedClient) { yield break; } yield return null; } NetworkManager.Singleton.Shutdown(false); } Plugin.Log.LogWarning((object)("No game to join at " + address)); menu.SetLoadingScreen(false, (RoomEnter)5, ""); } } [BepInPlugin("tech.rethunk.mortar.DevLaunch", "DevLaunch", "0.1.1")] [BepInProcess("Lethal Company.exe")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log { get; private set; } = null; internal static Launch Launch { get; private set; } = new Launch(); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Badge.Bind(((BaseUnityPlugin)this).Config); Launch = DevLaunch.Launch.From(Environment.GetCommandLineArgs()); Log.LogInfo((object)"DevLaunch 0.1.1 loaded"); if (Launch.Online.HasValue || Launch.Host || Launch.JoinAddress != null || Launch.Tile.HasValue) { Splash.Stop(); Menus.Hook(); Badge.Show(); } } } internal sealed class Launch { public bool? Online { get; private set; } public bool Host { get; private set; } public string? JoinAddress { get; private set; } public (int Index, int Count)? Tile { get; private set; } public static Launch From(string[] args) { Launch launch = new Launch(); for (int i = 0; i < args.Length; i++) { switch (args[i]) { case "--online": launch.Online = true; break; case "--lan": launch.Online = false; break; case "--host": launch.Host = true; break; case "--join": if (i + 1 < args.Length) { launch.JoinAddress = args[++i]; } break; case "--tile": if (i + 1 < args.Length) { string[] array = args[++i].Split('/'); if (array.Length == 2 && int.TryParse(array[0], out var result) && int.TryParse(array[1], out var result2) && result2 > 0 && result >= 1 && result <= result2) { launch.Tile = (result - 1, result2); } } break; } } return launch; } } internal static class Splash { private static readonly TimeSpan wait = TimeSpan.FromSeconds(30.0); private static volatile bool sceneLoaded; public static void Stop() { SceneManager.sceneLoaded += delegate { sceneLoaded = true; }; Thread thread = new Thread(Ask); thread.IsBackground = true; thread.Name = "DevLaunchSplash"; thread.Start(); } private static void Ask() { Stopwatch stopwatch = Stopwatch.StartNew(); while (!sceneLoaded && stopwatch.Elapsed < wait) { SplashScreen.Stop((StopBehavior)0); Thread.Sleep(10); } } } internal sealed class Tile : MonoBehaviour { private const int TitleBar = 40; private static Tile? instance; public static void Add(GameObject holder) { holder.AddComponent<Tile>(); } public static void Apply() { if ((Object)(object)instance != (Object)null) { (int, int)? tile = Plugin.Launch.Tile; if (tile.HasValue) { (int, int) valueOrDefault = tile.GetValueOrDefault(); ((MonoBehaviour)instance).StopAllCoroutines(); ((MonoBehaviour)instance).StartCoroutine(Place(valueOrDefault.Item1, valueOrDefault.Item2)); } } } private void Awake() { instance = this; Apply(); } private static IEnumerator Place(int index, int count) { DisplayInfo display = Screen.mainWindowDisplayInfo; RectInt area = (RectInt)((((RectInt)(ref display.workArea)).width > 0) ? display.workArea : new RectInt(0, 0, display.width, display.height)); int columns = (int)Math.Ceiling(Math.Sqrt(count)); int num = (int)Math.Ceiling((double)count / (double)columns); int width = ((RectInt)(ref area)).width / columns; int height = ((RectInt)(ref area)).height / num; Screen.SetResolution(width, height - 40, (FullScreenMode)3); yield return null; Vector2Int val = default(Vector2Int); ((Vector2Int)(ref val))..ctor(((RectInt)(ref area)).x + index % columns * width, ((RectInt)(ref area)).y + index / columns * height); Screen.MoveMainWindowTo(ref display, val); Plugin.Log.LogInfo((object)$"Window {index + 1} of {count}: {width}x{height - 40} at {((Vector2Int)(ref val)).x},{((Vector2Int)(ref val)).y} on {display.name}"); } } internal static class WindowTitle { private delegate bool WindowVisitor(IntPtr window, IntPtr data); private static readonly WindowVisitor visit = Rename; private static string title = ""; private static uint process; public static void Set(string text) { title = text; process = (uint)Process.GetCurrentProcess().Id; try { EnumWindows(visit, IntPtr.Zero); } catch (Exception ex) when (((ex is DllNotFoundException || ex is EntryPointNotFoundException) ? 1 : 0) != 0) { } } private static bool Rename(IntPtr window, IntPtr data) { GetWindowThreadProcessId(window, out var processId); if (processId == process && IsWindowVisible(window)) { SetWindowTextW(window, title); } return true; } [DllImport("user32.dll")] private static extern bool EnumWindows(WindowVisitor visitor, IntPtr data); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr window, out uint processId); [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr window); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern bool SetWindowTextW(IntPtr window, string text); } internal static class MyPluginInfo { public const string Id = "tech.rethunk.mortar.DevLaunch"; public const string Name = "DevLaunch"; public const string Version = "0.1.1"; } }