Decompiled source of SlipStreamerBot v1.2.1

BepInEx/plugins/MoSadie-SlipStreamerBot/com.mosadie.slipstreamerbot.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CrewmateSwapped;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using RegionVo;
using Subpixel.Events;
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("com.mosadie.slipstreamerbot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Send in-game events to Streamer.bot")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+13a8d356cd21888324b84b450d097cb8bf38df9f")]
[assembly: AssemblyProduct("SlipStreamer.bot")]
[assembly: AssemblyTitle("com.mosadie.slipstreamerbot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SlipStreamer.bot
{
	[BepInPlugin("com.mosadie.slipstreamerbot", "SlipStreamer.bot", "1.2.1")]
	[BepInProcess("Slipstream_Win.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private enum CaptaincyRequiredConfigValue
		{
			Inherit,
			Required,
			NotRequired
		}

		private enum EventType
		{
			GameLaunch,
			GameExit,
			JoinShip,
			StartFight,
			EndFight,
			NodeChange,
			ChoiceAvailable,
			KnockedOut,
			RunStarted,
			RunFailed,
			RunSucceeded,
			NextSector,
			ShopEntered,
			CrewmateCreated,
			CrewmateRemoved,
			CrewmateSwapped
		}

		private static ConfigEntry<string> streamerBotIp;

		private static ConfigEntry<int> streamerBotPort;

		private static ConfigEntry<string> streamerBotActionId;

		private static ConfigEntry<string> streamerBotActionName;

		private static ConfigEntry<bool> defaultCaptaincyRequired;

		private static HttpClient httpClient = new HttpClient();

		internal static ManualLogSource Log;

		private static Dictionary<EventType, ConfigEntry<int>> eventCooldownConfigs = new Dictionary<EventType, ConfigEntry<int>>();

		private static Dictionary<EventType, long> lastEventTime = new Dictionary<EventType, long>();

		public static readonly string COMPATIBLE_GAME_VERSION = "4.1556";

		private static Dictionary<EventType, ConfigEntry<CaptaincyRequiredConfigValue>> captaincyRequiredConfigs = new Dictionary<EventType, ConfigEntry<CaptaincyRequiredConfigValue>>();

		private void Awake()
		{
			try
			{
				Log = ((BaseUnityPlugin)this).Logger;
				Log.LogInfo((object)("Game version: " + Application.version));
				if (Application.version != COMPATIBLE_GAME_VERSION)
				{
					Log.LogError((object)"This version of SlipStreamer.bot is not compatible with the current game version. Please check for an updated version of the plugin.");
					return;
				}
				streamerBotIp = ((BaseUnityPlugin)this).Config.Bind<string>("StreamerBot", "Ip", "127.0.0.1", (ConfigDescription)null);
				streamerBotPort = ((BaseUnityPlugin)this).Config.Bind<int>("StreamerBot", "Port", 7474, (ConfigDescription)null);
				streamerBotActionId = ((BaseUnityPlugin)this).Config.Bind<string>("StreamerBot", "ActionId", "da524811-ff47-4493-afe6-67f27eff234d", "Action ID to execute on game events.");
				streamerBotActionName = ((BaseUnityPlugin)this).Config.Bind<string>("StreamerBot", "ActionName", "(Internal) Receive Event", "Action name to execute on game events.");
				foreach (EventType value in Enum.GetValues(typeof(EventType)))
				{
					eventCooldownConfigs[value] = ((BaseUnityPlugin)this).Config.Bind<int>("StreamerBot", $"EventCooldown_{value}", 0, $"Cooldown in ms before sending a duplicate {value} event. (ex. 5000 = 5 seconds) Set to 0 to disable cooldown.");
				}
				defaultCaptaincyRequired = ((BaseUnityPlugin)this).Config.Bind<bool>("Captaincy", "DefaultIsCaptainRequired", false, "Configure if you must be the captain of the ship to trigger Streamer.bot actions. This sets the requirement for any event configured to 'inherit' the setting.");
				foreach (EventType value2 in Enum.GetValues(typeof(EventType)))
				{
					if (value2 != 0 && value2 != EventType.GameExit && value2 != EventType.JoinShip)
					{
						captaincyRequiredConfigs[value2] = ((BaseUnityPlugin)this).Config.Bind<CaptaincyRequiredConfigValue>("Captaincy", $"IsCaptainRequired_{value2}", CaptaincyRequiredConfigValue.Inherit, $"Configure if you must be the captain of the ship to trigger Streamer.bot actions for the {value2} event. (Inherit = use from the DefaultIsCaptainRequired setting , Required = must be captain, NotRequired = does not need to be captain)");
					}
				}
				Harmony.CreateAndPatchAll(typeof(Plugin), (string)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin com.mosadie.slipstreamerbot is loaded!");
				Svc.Get<Events>().AddListener<ShipLoadedEvent>((Action<ShipLoadedEvent>)ShipLoadedEvent, 1);
				Svc.Get<Events>().AddListener<OrderGivenEvent>((Action<OrderGivenEvent>)OrderGivenEvent, 1);
				Svc.Get<Events>().AddListener<BattleStartEvent>((Action<BattleStartEvent>)BattleStartEvent, 1);
				Svc.Get<Events>().AddListener<BattleEndEvent>((Action<BattleEndEvent>)BattleEndEvent, 1);
				Svc.Get<Events>().AddListener<CampaignStartEvent>((Action<CampaignStartEvent>)CampaignStartEvent, 1);
				Svc.Get<Events>().AddListener<CampaignEndEvent>((Action<CampaignEndEvent>)CampaignEndEvent, 1);
				Svc.Get<Events>().AddListener<CampaignSectorChangeEvent>((Action<CampaignSectorChangeEvent>)CampaignSectorChangeEvent, 1);
				Svc.Get<Events>().AddListener<SectorNodeChangedEvent>((Action<SectorNodeChangedEvent>)SectorNodeChangedEvent, 1);
				Svc.Get<Events>().AddListener<CrewmateCreatedEvent>((Action<CrewmateCreatedEvent>)CrewmateCreatedEvent, 1);
				Svc.Get<Events>().AddListener<CrewmateRemovedEvent>((Action<CrewmateRemovedEvent>)CrewmateRemovedEvent, 1);
				Application.quitting += ApplicationQuitting;
				sendEvent(EventType.GameLaunch, new Dictionary<string, string>());
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("An error occurred during plugin startup: " + ex.Message));
			}
		}

		private static bool blockEvent(EventType eventType)
		{
			Log.LogInfo((object)$"Checking captaincy required for event {eventType} isCaptain:{getIsCaptain()}");
			try
			{
				if (!captaincyRequiredConfigs.ContainsKey(eventType))
				{
					Log.LogDebug((object)"Captaincy required config not found for event type. Defaulting to false.");
					return false;
				}
				if (captaincyRequiredConfigs[eventType].Value == CaptaincyRequiredConfigValue.Inherit)
				{
					return defaultCaptaincyRequired.Value && !getIsCaptain();
				}
				if (captaincyRequiredConfigs[eventType].Value == CaptaincyRequiredConfigValue.Required)
				{
					return !getIsCaptain();
				}
				if (captaincyRequiredConfigs[eventType].Value == CaptaincyRequiredConfigValue.NotRequired)
				{
					return false;
				}
				Log.LogError((object)$"Unknown captaincy required config value: {captaincyRequiredConfigs[eventType].Value}");
				return true;
			}
			catch (Exception ex)
			{
				Log.LogError((object)$"Error checking captaincy required for event {eventType}: {ex.Message}");
				return true;
			}
		}

		private static void sendEvent(EventType eventType, Dictionary<string, string> data)
		{
			if (blockEvent(eventType))
			{
				Log.LogInfo((object)$"Event {eventType} is blocked. Skipping.");
				return;
			}
			if (eventCooldownConfigs[eventType].Value > 0)
			{
				long num = DateTimeOffset.Now.ToUnixTimeMilliseconds();
				if (lastEventTime.ContainsKey(eventType) && num - lastEventTime[eventType] < eventCooldownConfigs[eventType].Value)
				{
					Log.LogInfo((object)$"Event {eventType} is on cooldown. Skipping.");
					return;
				}
				lastEventTime[eventType] = num;
			}
			try
			{
				data.Add("eventType", eventType.ToString());
				string text = JsonConvert.SerializeObject((object)new
				{
					action = new
					{
						id = streamerBotActionId.Value,
						name = streamerBotActionName.Value
					},
					args = data
				});
				Log.LogInfo((object)$"Sending event {eventType} to StreamerBot: {streamerBotIp.Value}:{streamerBotPort.Value} with data: {text}");
				httpClient.PostAsync($"http://{streamerBotIp.Value}:{streamerBotPort.Value}/DoAction", new StringContent(text));
			}
			catch (HttpRequestException ex)
			{
				Log.LogError((object)("Error sending event to StreamerBot: " + ex.Message));
			}
		}

		private void ShipLoadedEvent(ShipLoadedEvent e)
		{
			try
			{
				sendEvent(EventType.JoinShip, new Dictionary<string, string>());
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending ShipLoadedEvent: " + ex.Message));
			}
		}

		private void OrderGivenEvent(OrderGivenEvent e)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			try
			{
				if ((int)e.Order.Type == 6)
				{
					sendEvent(EventType.KnockedOut, new Dictionary<string, string> { 
					{
						"message",
						e.Order.Message
					} });
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending OrderGivenEvent: " + ex.Message));
			}
		}

		private void ApplicationQuitting()
		{
			try
			{
				sendEvent(EventType.GameExit, new Dictionary<string, string>());
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending GameExit: " + ex.Message));
			}
		}

		private void BattleStartEvent(BattleStartEvent e)
		{
			try
			{
				if (e.Scenario == null || e.Scenario.Battle == null)
				{
					Log.LogError((object)"BattleStartEvent: Scenario or Battle is null");
					return;
				}
				sendEvent(EventType.StartFight, new Dictionary<string, string>
				{
					{
						"enemy",
						e.Scenario.Battle.Metadata.EnemyName
					},
					{
						"invaders",
						e.Scenario.Battle.Metadata.InvaderDescription
					},
					{
						"intel",
						e.Scenario.Battle.Metadata.IntelDescription
					},
					{
						"threatLevel",
						e.Scenario.Battle.Metadata.ThreatLevel.ToString()
					},
					{
						"speedLevel",
						e.Scenario.Battle.Metadata.SpeedLevel.ToString()
					},
					{
						"cargoLevel",
						e.Scenario.Battle.Metadata.CargoLevel.ToString()
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending BattleStartEvent: " + ex.Message));
			}
		}

		private void BattleEndEvent(BattleEndEvent e)
		{
			try
			{
				sendEvent(EventType.EndFight, new Dictionary<string, string> { 
				{
					"outcome",
					((object)(BattleOutcome)(ref e.Outcome)).ToString()
				} });
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending BattleEndEvent: " + ex.Message));
			}
		}

		private void CampaignStartEvent(CampaignStartEvent e)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Dictionary<string, string> obj = new Dictionary<string, string> { 
				{
					"campaign",
					e.Campaign.CampaignId.ToString()
				} };
				Root regionVo = e.Campaign.CaptainCampaign.RegionVo;
				RegionMetadataVo metadata = ((Root)(ref regionVo)).Metadata;
				obj.Add("region", ((RegionMetadataVo)(ref metadata)).Name);
				sendEvent(EventType.RunStarted, obj);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CampaignStartEvent: " + ex.Message));
			}
		}

		private void CampaignEndEvent(CampaignEndEvent e)
		{
			try
			{
				if (e.Victory)
				{
					sendEvent(EventType.RunSucceeded, new Dictionary<string, string>());
				}
				else
				{
					sendEvent(EventType.RunFailed, new Dictionary<string, string>());
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CampaignEndEvent: " + ex.Message));
			}
		}

		private void CampaignSectorChangeEvent(CampaignSectorChangeEvent e)
		{
			try
			{
				sendEvent(EventType.NextSector, new Dictionary<string, string>
				{
					{
						"sectorIndex",
						e.Campaign.CurrentSectorIndex.ToString()
					},
					{
						"sectorName",
						((SectorDefVo)(ref e.Campaign.CurrentSectorVo.Definition)).Name
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CampaignSectorChangeEvent: " + ex.Message));
			}
		}

		private void SectorNodeChangedEvent(SectorNodeChangedEvent e)
		{
			try
			{
				if (e.CampaignVo == null || ((object)(SectorNodeVo)(ref e.CampaignVo.CurrentNodeVo)).Equals((object?)null))
				{
					return;
				}
				ScenarioWrapperVo currentScenarioVo = e.CampaignVo.CurrentScenarioVo;
				if (currentScenarioVo == null)
				{
					Log.LogError((object)("Scenario not found: " + e.CampaignVo.CurrentNodeVo.ScenarioKey));
					return;
				}
				if (currentScenarioVo.Encounter != null)
				{
					sendEvent(EventType.ChoiceAvailable, new Dictionary<string, string>
					{
						{
							"isBacktrack",
							e.IsBacktrack.ToString()
						},
						{
							"scenarioKey",
							e.CampaignVo.CurrentNodeVo.ScenarioKey
						},
						{
							"visited",
							e.CampaignVo.CurrentNodeVo.Visited.ToString()
						},
						{
							"completed",
							e.CampaignVo.CurrentNodeVo.Completed.ToString()
						},
						{
							"captainVictory",
							e.CampaignVo.CurrentNodeVo.CaptainVictory.ToString()
						},
						{
							"scenarioName",
							currentScenarioVo.Encounter.Name
						},
						{
							"scenarioDescription",
							currentScenarioVo.Encounter.Details.Full.Description
						},
						{
							"proposition",
							currentScenarioVo.Encounter.Proposition
						},
						{
							"choice1",
							currentScenarioVo.Encounter.Option1.Action
						},
						{
							"choice2",
							currentScenarioVo.Encounter.Option2.Action
						}
					});
				}
				else if (currentScenarioVo.Outpost != null)
				{
					Dictionary<string, string> dictionary = new Dictionary<string, string>
					{
						{
							"isBacktrack",
							e.IsBacktrack.ToString()
						},
						{
							"scenarioKey",
							e.CampaignVo.CurrentNodeVo.ScenarioKey
						},
						{
							"visited",
							e.CampaignVo.CurrentNodeVo.Visited.ToString()
						},
						{
							"name",
							currentScenarioVo.Outpost.Name
						},
						{
							"description",
							currentScenarioVo.Outpost.Details.Full.Description
						},
						{
							"inventorySize",
							currentScenarioVo.Outpost.Inventory.Length.ToString()
						}
					};
					for (int i = 0; i < currentScenarioVo.Outpost.Inventory.Length; i++)
					{
						dictionary.Add($"inventory{i}_type", ((object)(EconomyType)(ref currentScenarioVo.Outpost.Inventory[i].Type)).ToString());
						dictionary.Add($"inventory{i}_price", currentScenarioVo.Outpost.Inventory[i].PricePerUnit.ToString());
						dictionary.Add($"inventory{i}_subtype", currentScenarioVo.Outpost.Inventory[i].SubType.ToString());
					}
					sendEvent(EventType.ShopEntered, dictionary);
				}
				sendEvent(EventType.NodeChange, new Dictionary<string, string>
				{
					{
						"isBacktrack",
						e.IsBacktrack.ToString()
					},
					{
						"scenarioKey",
						e.CampaignVo.CurrentNodeVo.ScenarioKey
					},
					{
						"visited",
						e.CampaignVo.CurrentNodeVo.Visited.ToString()
					},
					{
						"completed",
						e.CampaignVo.CurrentNodeVo.Completed.ToString()
					},
					{
						"captainVictory",
						e.CampaignVo.CurrentNodeVo.CaptainVictory.ToString()
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending SectorNodeChangedEvent: " + ex.Message));
			}
		}

		private void CrewmateCreatedEvent(CrewmateCreatedEvent e)
		{
			try
			{
				string value = ((e.Crewmate.Client != null) ? e.Crewmate.Client.Player.DisplayName : "Crew");
				sendEvent(EventType.CrewmateCreated, new Dictionary<string, string>
				{
					{ "name", value },
					{
						"id",
						e.Crewmate.CrewmateId.ToString()
					},
					{
						"level",
						e.CrewmateVo.Progression.Level.ToString()
					},
					{
						"xp",
						e.CrewmateVo.Progression.TotalXp.ToString()
					},
					{
						"archetype",
						e.CrewmateVo.ArchetypeId
					},
					{
						"statHealth",
						e.Crewmate.Stats.MaxHealth.ToString()
					},
					{
						"statShields",
						e.Crewmate.Stats.MaxShields.ToString()
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CrewmateCreatedEvent: " + ex.Message));
			}
		}

		private void CrewmateRemovedEvent(CrewmateRemovedEvent e)
		{
			try
			{
				string value = ((e.Crewmate.Client != null) ? e.Crewmate.Client.Player.DisplayName : "Crew");
				sendEvent(EventType.CrewmateRemoved, new Dictionary<string, string>
				{
					{ "name", value },
					{
						"id",
						e.Crewmate.CrewmateId.ToString()
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CrewmateRemovedEvent: " + ex.Message));
			}
		}

		private static bool getIsCaptain()
		{
			try
			{
				if (Svc.Get<MpSvc>() == null)
				{
					Log.LogError((object)"An error occurred handling self crew. null MpSvc.");
					return false;
				}
				MpCaptainController captains = Svc.Get<MpSvc>().Captains;
				if (captains == null || captains.CaptainClient == null)
				{
					return false;
				}
				return captains.CaptainClient.IsLocal;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("An error occurred while checking if the crewmate is the captain: " + ex.Message));
				return false;
			}
		}

		[HarmonyPatch(typeof(MpCrewController), "OnCrewmateSwapped")]
		[HarmonyPostfix]
		[HarmonyWrapSafe]
		private static void OnCrewmateSwapped(ref MpCrewController __instance, Notification<Payload> notif)
		{
			Log.LogMessage((object)$"Crewmate Swapped Test. ID: {notif.Payload.SessionId}");
			Crewmate crewmateById = __instance.GetCrewmateById(notif.Payload.SessionId);
			if ((Object)(object)crewmateById == (Object)null)
			{
				Log.LogError((object)$"Crewmate not found by ID: {notif.Payload.SessionId}");
				return;
			}
			string value = ((crewmateById.Client != null) ? crewmateById.Client.Player.DisplayName : "Crew");
			try
			{
				sendEvent(EventType.CrewmateSwapped, new Dictionary<string, string>
				{
					{ "name", value },
					{
						"id",
						crewmateById.CrewmateId.ToString()
					},
					{
						"level",
						notif.Payload.NewCrewmateVo.Progression.Level.ToString()
					},
					{
						"xp",
						notif.Payload.NewCrewmateVo.Progression.TotalXp.ToString()
					},
					{
						"archetype",
						notif.Payload.NewCrewmateVo.ArchetypeId
					},
					{
						"statHealth",
						notif.Payload.NewCrewmateVo.Stats.MaxHealth.ToString()
					},
					{
						"statShields",
						notif.Payload.NewCrewmateVo.Stats.MaxShields.ToString()
					}
				});
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error sending CrewmateSwappedEvent: " + ex.Message));
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.mosadie.slipstreamerbot";

		public const string PLUGIN_NAME = "SlipStreamer.bot";

		public const string PLUGIN_VERSION = "1.2.1";
	}
}