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 ShipLightsTerminal v1.0.2
ShipLightsTerminal.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.UI; [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("ShipLightsTerminal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("ShipLightsTerminal")] [assembly: AssemblyTitle("ShipLightsTerminal")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.2.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 ShipLightsTerminal { internal static class HostModGate { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnMessage; public static Action<ulong> <1>__OnClientConnected; } public const string MessageName = "MrGlim.ShipLightsTerminal"; private const byte OpHostHello = 0; private const byte OpClientSyncRequest = 1; private static bool _registered; private static bool _clientConnectedHooked; private static bool _requestedSync; private static bool _clientHostEnabled; public static bool HostHasMod { get { EnsureRegistered(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return true; } if (singleton.IsServer || singleton.IsHost) { return LocalEnabled(); } return _clientHostEnabled; } } public static bool FeaturesActive => HostHasMod; private static bool LocalEnabled() { if (Plugin.Enabled != null) { return Plugin.Enabled.Value; } return false; } public static void Reset() { _registered = false; _clientConnectedHooked = false; _requestedSync = false; _clientHostEnabled = false; } public static void EnsureRegistered() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { _registered = false; _clientConnectedHooked = false; return; } if (!_registered) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnMessage; if (obj == null) { HandleNamedMessageDelegate val = OnMessage; <>O.<0>__OnMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("MrGlim.ShipLightsTerminal", (HandleNamedMessageDelegate)obj); _registered = true; Plugin.Log.LogInfo((object)"MrGlim.ShipLightsTerminal net handler registered."); } if (singleton.IsServer && !_clientConnectedHooked) { singleton.OnClientConnectedCallback += OnClientConnected; _clientConnectedHooked = true; } if (!singleton.IsServer && singleton.IsConnectedClient && !_requestedSync) { _requestedSync = true; RequestSync(); } } private static void OnClientConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && clientId != singleton.LocalClientId) { SendHello(clientId); } } private static void RequestSync() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !singleton.IsServer) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b = 1; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("MrGlim.ShipLightsTerminal", 0uL, val, (NetworkDelivery)2); ((FastBufferWriter)(ref val)).Dispose(); } } private static void SendHello(ulong clientId) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b = 0; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); bool flag = LocalEnabled(); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref flag, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("MrGlim.ShipLightsTerminal", clientId, val, (NetworkDelivery)2); ((FastBufferWriter)(ref val)).Dispose(); } } private static void OnMessage(ulong sender, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; switch (b) { case 1: if ((Object)(object)singleton != (Object)null && singleton.IsServer) { SendHello(sender); } break; case 0: { bool clientHostEnabled = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref clientHostEnabled, default(ForPrimitives)); if ((Object)(object)singleton != (Object)null && !singleton.IsServer) { _clientHostEnabled = clientHostEnabled; Plugin.Log.LogInfo((object)("Host hello: enabled=" + clientHostEnabled)); } break; } } } } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] internal static class HostModGateDisconnectPatch { public static void Prefix() { HostModGate.Reset(); Plugin.Log.LogInfo((object)"MrGlim.ShipLightsTerminal gate reset on disconnect."); } } internal static class ManualPatches { internal static void Apply(Harmony harmony) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown try { Prefix(typeof(Terminal), "OnSubmit", typeof(OnSubmitPatch), "Prefix"); Prefix(typeof(Terminal), "ParseWord", typeof(ParseWordPatch), "Prefix", new Type[2] { typeof(string), typeof(int) }); Prefix(typeof(Terminal), "ParsePlayerSentence", typeof(ParseSentencePatch), "Prefix"); Prefix(typeof(Terminal), "LoadNewNode", typeof(LoadNewNodePatch), "Prefix"); Postfix(typeof(Terminal), "Awake", typeof(TerminalLifecyclePatch), "AwakePostfix"); Postfix(typeof(Terminal), "Start", typeof(TerminalLifecyclePatch), "StartPostfix"); MethodInfo methodInfo = AccessTools.Method(typeof(GameNetworkManager), "Disconnect", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(HostModGateDisconnectPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched GameNetworkManager.Disconnect for host gate reset"); } MethodInfo methodInfo2 = AccessTools.Method(typeof(StartOfRound), "Start", (Type[])null, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(HostModGateStartPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched StartOfRound.Start for host gate register"); } } catch (Exception arg) { Plugin.Log.LogError((object)$"ManualPatches.Apply failed: {arg}"); } void Postfix(Type type, string name, Type patchType, string method) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown MethodInfo methodInfo3 = AccessTools.Method(type, name, (Type[])null, (Type[])null); Plugin.Log.LogInfo((object)("Resolve " + type.Name + "." + name + " => " + ((methodInfo3 == null) ? "NULL" : "ok"))); if (!(methodInfo3 == null)) { harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(patchType, method, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("Patched " + type.Name + "." + name + " postfix " + method)); } } void Prefix(Type type, string name, Type patchType, string method, Type[]? args = null) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown MethodInfo methodInfo3 = ((args == null) ? AccessTools.Method(type, name, (Type[])null, (Type[])null) : AccessTools.Method(type, name, args, (Type[])null)); Plugin.Log.LogInfo((object)("Resolve " + type.Name + "." + name + " => " + ((methodInfo3 == null) ? "NULL" : (methodInfo3.IsPublic ? "public" : "nonpublic")))); if (!(methodInfo3 == null)) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(patchType, method, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("Patched " + type.Name + "." + name + " prefix " + method)); } } } } internal static class TerminalInput { internal static string? LastSubmitted; private static readonly HashSet<string> Commands = new HashSet<string>(StringComparer.Ordinal) { "lights", "light", "ship lights", "lightson", "lights on", "light on", "onlights", "turn on lights", "lightsoff", "lights off", "light off", "offlights", "turn off lights", "togglelights", "toggle lights" }; internal static string Normalize(string? raw) { if (string.IsNullOrEmpty(raw)) { return ""; } return Regex.Replace(Regex.Replace(raw.ToLowerInvariant(), "[^a-z0-9\\s]", " "), "\\s+", " ").Trim(); } internal static string Extract(Terminal terminal) { try { string text = (((Object)(object)terminal.screenText != (Object)null) ? terminal.screenText.text : null); if (string.IsNullOrEmpty(text)) { return ""; } if (terminal.textAdded > 0 && text.Length >= terminal.textAdded) { return Normalize(text.Substring(text.Length - terminal.textAdded)); } int num = text.LastIndexOf('\n'); return Normalize(((num >= 0) ? text.Substring(num + 1) : text).Trim().TrimStart('>', ' ')); } catch { return ""; } } internal static bool IsLightsCommand(string input) { return Commands.Contains(input); } } internal static class OnSubmitPatch { public static bool Prefix(Terminal __instance) { try { string text = (TerminalInput.LastSubmitted = TerminalInput.Extract(__instance)); Plugin.Log.LogInfo((object)$"[OnSubmit] captured='{text}' enabled={Plugin.Enabled?.Value}"); if (!HostModGate.FeaturesActive) { return true; } if (!TerminalInput.IsLightsCommand(text)) { return true; } string text2 = LightActions.Run(text); Plugin.Log.LogInfo((object)("[OnSubmit] handled '" + text + "'")); __instance.LoadNewNode(LightActions.CreateDisplayNode(text2)); LightActions.ReadyForNextCommand(__instance); return false; } catch (Exception arg) { Plugin.Log.LogWarning((object)$"[OnSubmit] {arg}"); return true; } } } internal static class ParseWordPatch { public static bool Prefix(string playerWord, int specificityRequired, ref TerminalKeyword __result) { if (!HostModGate.FeaturesActive) { return true; } try { string text = TerminalInput.Normalize(playerWord); Plugin.V($"[ParseWord] '{playerWord}' -> '{text}' spec={specificityRequired}"); if (!TerminalInput.IsLightsCommand(text)) { return true; } __result = LightActions.GetKeyword(text); Plugin.Log.LogInfo((object)("[ParseWord] returning lights keyword for '" + text + "'")); return false; } catch (Exception arg) { Plugin.Log.LogWarning((object)$"[ParseWord] {arg}"); return true; } } } internal static class ParseSentencePatch { public static bool Prefix(Terminal __instance, ref TerminalNode __result) { if (!HostModGate.FeaturesActive) { return true; } try { string text = TerminalInput.LastSubmitted; if (string.IsNullOrEmpty(text)) { text = TerminalInput.Extract(__instance); } Plugin.V("[ParseSentence] input='" + text + "'"); if (string.IsNullOrEmpty(text) || !TerminalInput.IsLightsCommand(text)) { return true; } string text2 = LightActions.Run(text); __result = LightActions.CreateDisplayNode(text2); TerminalInput.LastSubmitted = null; Plugin.Log.LogInfo((object)("[ParseSentence] handled '" + text + "'")); return false; } catch (Exception arg) { Plugin.Log.LogWarning((object)$"[ParseSentence] {arg}"); return true; } } } internal static class LoadNewNodePatch { private static int _reentry; public static void Prefix(TerminalNode node) { if ((Object)(object)node == (Object)null || _reentry > 0) { return; } try { if (!HostModGate.FeaturesActive) { return; } if (!LightActions.TryGetCommandForNode(node, out string cmd)) { Plugin.V("[LoadNewNode] unrelated"); return; } Plugin.Log.LogInfo((object)("[LoadNewNode] lights keyword node for '" + cmd + "' — running action")); _reentry++; try { string text = LightActions.Run(cmd); string text2 = (text.EndsWith("\n") ? text : (text + "\n")); if (!text2.EndsWith("\n\n")) { text2 += "\n"; } node.displayText = text2; node.clearPreviousText = true; node.acceptAnything = false; node.overrideOptions = false; } finally { _reentry--; } } catch (Exception arg) { Plugin.Log.LogWarning((object)$"[LoadNewNode] {arg}"); } } } internal static class TerminalLifecyclePatch { public static void AwakePostfix(Terminal __instance) { Plugin.Log.LogInfo((object)"[Terminal.Awake] postfix hit"); LightActions.EnsureKeywordsRegistered(__instance); LightActions.EnsureHelpText(__instance); } public static void StartPostfix(Terminal __instance) { HostModGate.EnsureRegistered(); Plugin.Log.LogInfo((object)"[Terminal.Start] postfix hit"); LightActions.EnsureKeywordsRegistered(__instance); LightActions.EnsureHelpText(__instance); } } internal static class LightActions { private static readonly Dictionary<string, TerminalKeyword> Keywords = new Dictionary<string, TerminalKeyword>(); private static readonly Dictionary<TerminalNode, string> NodeCommands = new Dictionary<TerminalNode, string>(); private static bool _registered; private static bool _helpInjected; private const string HelpMarker = "[ShipLightsTerminal]"; private const string HelpFingerprint = ">LIGHTS"; private const string HelpBlock = ">LIGHTS\nToggle ship lights (also LIGHTSON / LIGHTSOFF).\n\n"; internal static TerminalNode CreateDisplayNode(string text) { TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>(); string text2 = text ?? ""; if (!text2.EndsWith("\n")) { text2 += "\n"; } if (!text2.EndsWith("\n\n")) { text2 += "\n"; } obj.displayText = text2; obj.clearPreviousText = true; obj.maxCharactersToType = 80; obj.acceptAnything = false; obj.overrideOptions = false; return obj; } internal static void ReadyForNextCommand(Terminal terminal) { try { if (!((Object)(object)terminal?.screenText == (Object)null)) { terminal.screenText.ActivateInputField(); ((Selectable)terminal.screenText).Select(); int num = ((terminal.screenText.text != null) ? terminal.screenText.text.Length : 0); terminal.screenText.caretPosition = num; terminal.screenText.selectionAnchorPosition = num; terminal.screenText.selectionFocusPosition = num; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[ReadyForNextCommand] " + ex.Message)); } } internal static bool TryGetCommandForNode(TerminalNode node, out string cmd) { if (NodeCommands.TryGetValue(node, out cmd)) { return true; } string text = node.displayText ?? ""; if (text.StartsWith("Ship lights command:", StringComparison.OrdinalIgnoreCase)) { cmd = TerminalInput.Normalize(text.Substring("Ship lights command:".Length)); return TerminalInput.IsLightsCommand(cmd); } cmd = ""; return false; } internal static TerminalKeyword GetKeyword(string word) { string text = (word.Contains(" ") ? word.Split(' ')[0] : word); if (Keywords.TryGetValue(text, out TerminalKeyword value) && (Object)(object)value != (Object)null) { return value; } EnsureKeyword(text); return Keywords[text]; } private static void EnsureKeyword(string word) { if (!Keywords.ContainsKey(word)) { TerminalNode val = CreateDisplayNode("Ship lights command: " + word + "\n"); NodeCommands[val] = word; TerminalKeyword val2 = ScriptableObject.CreateInstance<TerminalKeyword>(); val2.word = word; val2.isVerb = false; val2.specialKeywordResult = val; Keywords[word] = val2; } } internal static void EnsureKeywordsRegistered(Terminal terminal) { try { if (terminal?.terminalNodes?.allKeywords == null) { Plugin.Log.LogInfo((object)"Keyword register skipped: allKeywords null"); return; } EnsureKeyword("lights"); EnsureKeyword("light"); EnsureKeyword("lightson"); EnsureKeyword("lightsoff"); EnsureKeyword("onlights"); EnsureKeyword("offlights"); EnsureKeyword("togglelights"); if (_registered) { Plugin.V("Keywords already registered"); return; } List<TerminalKeyword> list = new List<TerminalKeyword>(terminal.terminalNodes.allKeywords); string[] array = new string[3] { "lights", "lightson", "lightsoff" }; foreach (string word in array) { if (Keywords.TryGetValue(word, out TerminalKeyword value) && !((Object)(object)value == (Object)null) && !list.Exists((TerminalKeyword k) => (Object)(object)k != (Object)null && k.word == word)) { list.Add(value); } } terminal.terminalNodes.allKeywords = list.ToArray(); _registered = true; Plugin.Log.LogInfo((object)$"Registered lights keywords (allKeywords={list.Count}, published=lights/lightson/lightsoff, trackedNodes={NodeCommands.Count})"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("EnsureKeywordsRegistered failed: " + ex.Message)); } } internal static void EnsureHelpText(Terminal terminal) { try { if (terminal?.terminalNodes?.specialNodes == null) { return; } int num = 0; for (int i = 0; i < terminal.terminalNodes.specialNodes.Count; i++) { if (TryApplyHelpToNode(terminal.terminalNodes.specialNodes[i], $"specialNodes[{i}]")) { num++; } } TerminalKeyword[] allKeywords = terminal.terminalNodes.allKeywords; if (allKeywords != null) { TerminalKeyword[] array = allKeywords; foreach (TerminalKeyword val in array) { if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.word)) { string word = val.word; if ((string.Equals(word, "help", StringComparison.OrdinalIgnoreCase) || string.Equals(word, "other", StringComparison.OrdinalIgnoreCase) || string.Equals(word, "others", StringComparison.OrdinalIgnoreCase)) && TryApplyHelpToNode(val.specialKeywordResult, "keyword:" + word)) { num++; } } } } if (num > 0) { _helpInjected = true; } else if (!_helpInjected) { Plugin.Log.LogInfo((object)"[Help] no command-list page found yet"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Help] " + ex.Message)); } } private static bool IsCommandListPage(string page) { if (string.IsNullOrEmpty(page)) { return false; } bool num = page.IndexOf(">STORE", StringComparison.OrdinalIgnoreCase) >= 0; bool flag = page.IndexOf(">BESTIARY", StringComparison.OrdinalIgnoreCase) >= 0; bool flag2 = page.IndexOf(">STORAGE", StringComparison.OrdinalIgnoreCase) >= 0; bool flag3 = page.IndexOf(">OTHER", StringComparison.OrdinalIgnoreCase) >= 0; if (!num || !(flag || flag2 || flag3)) { return false; } return true; } private static bool TryApplyHelpToNode(TerminalNode? node, string label) { if ((Object)(object)node == (Object)null || string.IsNullOrEmpty(node.displayText)) { return false; } string text = StripLegacyHelpHeader(node.displayText); if (!IsCommandListPage(text)) { string text2 = StripLightsHelp(text); if (text2 != node.displayText) { node.displayText = text2; Plugin.Log.LogInfo((object)("[Help] stripped lights docs from non-list " + label)); } return false; } if (text.IndexOf(">LIGHTS", StringComparison.OrdinalIgnoreCase) >= 0) { if (text != node.displayText) { node.displayText = text; } return false; } node.displayText = InjectHelp(text); Plugin.Log.LogInfo((object)("[Help] injected lights docs into " + label)); return true; } private static string StripLegacyHelpHeader(string page) { if (string.IsNullOrEmpty(page) || page.IndexOf("[ShipLightsTerminal]", StringComparison.Ordinal) < 0) { return page; } return page.Replace("[ShipLightsTerminal]\n\n", "").Replace("[ShipLightsTerminal]\n", "").Replace("[ShipLightsTerminal]", ""); } private static string StripLightsHelp(string page) { if (string.IsNullOrEmpty(page) || page.IndexOf(">LIGHTS", StringComparison.OrdinalIgnoreCase) < 0) { return page; } if (page.Contains(">LIGHTS\nToggle ship lights (also LIGHTSON / LIGHTSOFF).\n\n")) { return page.Replace(">LIGHTS\nToggle ship lights (also LIGHTSON / LIGHTSOFF).\n\n", ""); } int num = page.IndexOf(">LIGHTS", StringComparison.OrdinalIgnoreCase); if (num < 0) { return page; } int num2 = page.IndexOf("\n\n", num); num2 = ((num2 >= 0) ? (num2 + 2) : page.Length); int num3 = num; if (num3 >= 2 && page[num3 - 2] == '\n' && page[num3 - 1] == '\n') { num3 -= 2; } else if (num3 >= 1 && page[num3 - 1] == '\n') { num3--; } return page.Substring(0, num3) + page.Substring(num2); } private static string InjectHelp(string page) { page = StripLegacyHelpHeader(page); int num = page.IndexOf(">OTHER", StringComparison.OrdinalIgnoreCase); if (num < 0) { num = page.IndexOf("OTHER", StringComparison.OrdinalIgnoreCase); } if (num >= 0) { int num2 = page.IndexOf("\n\n", num); if (num2 > num) { int num3 = num2 + 2; return page.Substring(0, num3) + ">LIGHTS\nToggle ship lights (also LIGHTSON / LIGHTSOFF).\n\n" + page.Substring(num3); } } if (!page.EndsWith("\n")) { page += "\n"; } if (!page.EndsWith("\n\n")) { page += "\n"; } return page + ">LIGHTS\nToggle ship lights (also LIGHTSON / LIGHTSOFF).\n\n"; } internal static string Run(string input) { switch (input) { case "lights": case "light": case "ship lights": case "togglelights": case "toggle lights": return Toggle(); case "light on": case "onlights": case "lightson": case "lights on": case "turn on lights": return Set(wantOn: true); case "light off": case "offlights": case "lightsoff": case "lights off": case "turn off lights": return Set(wantOn: false); default: return "Unknown lights command.\n"; } } private static ShipLights? ResolveLights() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.shipRoomLights != (Object)null) { return instance.shipRoomLights; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Lights] StartOfRound.shipRoomLights: " + ex.Message)); } try { return Object.FindObjectOfType<ShipLights>(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Lights] FindObjectOfType<ShipLights>: " + ex2.Message)); return null; } } private static string Toggle() { ShipLights val = ResolveLights(); if ((Object)(object)val == (Object)null) { return "Ship lights not available.\n"; } bool areLightsOn = val.areLightsOn; try { val.SetShipLightsServerRpc(!areLightsOn); Plugin.Log.LogInfo((object)$"[Lights] Toggle via SetShipLightsServerRpc({!areLightsOn}); wasOn={areLightsOn}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Lights] SetShipLightsServerRpc toggle failed: " + ex.Message + "; trying ToggleShipLights")); try { val.ToggleShipLights(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Lights] ToggleShipLights: " + ex2.Message)); return "Ship lights not available.\n"; } } bool areLightsOn2 = val.areLightsOn; bool flag = !areLightsOn; try { flag = ((val.areLightsOn != areLightsOn) ? val.areLightsOn : (!areLightsOn)); } catch { } Plugin.V($"[Lights] after toggle areLightsOn={areLightsOn2} reportOn={flag}"); if (!flag) { return "Ship lights toggled (now OFF).\n"; } return "Ship lights toggled (now ON).\n"; } private static string Set(bool wantOn) { ShipLights val = ResolveLights(); if ((Object)(object)val == (Object)null) { return "Ship lights not available.\n"; } Plugin.Log.LogInfo((object)$"[Lights] Set wantOn={wantOn}, areLightsOn={val.areLightsOn}"); try { val.SetShipLightsServerRpc(wantOn); Plugin.Log.LogInfo((object)$"[Lights] SetShipLightsServerRpc({wantOn})"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Lights] SetShipLightsServerRpc failed: " + ex.Message + "; trying SetShipLightsBoolean")); try { val.SetShipLightsBoolean(wantOn); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Lights] SetShipLightsBoolean: " + ex2.Message)); return "Ship lights not available.\n"; } } if (!wantOn) { return "Ship lights are now OFF.\n"; } return "Ship lights are now ON.\n"; } } internal static class HostModGateStartPatch { public static void Postfix() { HostModGate.EnsureRegistered(); } } [BepInPlugin("com.benhough.lethal.ShipLightsTerminal", "ShipLightsTerminal", "1.0.2")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.benhough.lethal.ShipLightsTerminal"; public const string ModName = "ShipLightsTerminal"; public const string ModVersion = "1.0.2"; private readonly Harmony _harmony = new Harmony("com.benhough.lethal.ShipLightsTerminal"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<bool> Verbose { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable terminal commands: lights / lightson / lightsoff / togglelights."); Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log terminal/lights traces."); ManualPatches.Apply(_harmony); _harmony.PatchAll(typeof(HostModGateDisconnectPatch)); Log.LogInfo((object)string.Format("{0} v{1} loaded. Verbose={2}", "ShipLightsTerminal", "1.0.2", Verbose.Value)); } internal static void V(string msg) { if (Verbose != null && Verbose.Value) { Log.LogInfo((object)msg); } } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.benhough.lethal.ShipLightsTerminal"; public const string PLUGIN_NAME = "ShipLightsTerminal"; public const string PLUGIN_VERSION = "1.0.2"; } }