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 MoCore;
using Newtonsoft.Json;
using RegionVo;
using RelayedMessages;
using Requests.Campaigns;
using Subpixel;
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.3.0")]
[assembly: AssemblyInformationalVersion("1.2.3+ba6fb8d15921ae8b3ec8ff93c2aeba6fe557965d")]
[assembly: AssemblyProduct("SlipStreamer.bot")]
[assembly: AssemblyTitle("com.mosadie.slipstreamerbot")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.3.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.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInProcess("Slipstream_Win.exe")]
public class Plugin : BaseUnityPlugin, MoPlugin
{
private enum CaptaincyRequiredConfigValue
{
Inherit,
Required,
NotRequired
}
private enum EventType
{
GameLaunch,
GameExit,
JoinShip,
StartFight,
EndFight,
NodeChange,
ChoiceAvailable,
CustomOrder,
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.1579";
public static readonly string GAME_VERSION_URL = "https://raw.githubusercontent.com/MoSadie/SlipStreamer.bot/refs/heads/main/versions.json";
private static Dictionary<EventType, ConfigEntry<CaptaincyRequiredConfigValue>> captaincyRequiredConfigs = new Dictionary<EventType, ConfigEntry<CaptaincyRequiredConfigValue>>();
private void Awake()
{
try
{
Log = ((BaseUnityPlugin)this).Logger;
if (!MoCore.RegisterPlugin((MoPlugin)(object)this))
{
Log.LogError((object)"Failed to register plugin with MoCore. Please check the logs for more information.");
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_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Invalid comparison between Unknown and I4
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Invalid comparison between Unknown and I4
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
try
{
OrderType type = e.Order.Type;
if ((int)type != 6)
{
if ((int)type != 7)
{
return;
}
string value = "Unknown";
string value2 = null;
bool flag = false;
MpSvc val = Svc.Get<MpSvc>();
if (val != null)
{
SlipClient clientByClientId = val.Clients.GetClientByClientId(e.Order.SenderClientId);
if (clientByClientId != null && clientByClientId.Player != null)
{
value = ((clientByClientId.Player.DisplayName != null) ? clientByClientId.Player.DisplayName : "Unknown");
value2 = ((clientByClientId.Player.ProfileImage != null) ? clientByClientId.Player.ProfileImage : null);
flag = val.Captains.CaptainClient != null && ((object)(ClientId)(ref val.Captains.CaptainClient.ClientId)).Equals((object?)clientByClientId.ClientId);
}
}
sendEvent(EventType.CustomOrder, new Dictionary<string, string>
{
{
"message",
e.Order.Message
},
{ "senderDisplayName", value },
{ "senderProfileimage", value2 },
{
"senderIsCaptain",
flag.ToString()
}
});
}
else
{
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_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: 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_0043: 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 CampaignStartRelayed(StartCampaignRelayed e)
{
//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.Result.Campaign.CampaignId.ToString()
} };
RegionMetadataVo metadata = ((Root)(ref e.Result.Campaign.RegionVo)).Metadata;
obj.Add("region", ((RegionMetadataVo)(ref metadata)).Name);
sendEvent(EventType.RunStarted, obj);
}
catch (Exception ex)
{
Log.LogError((object)("Error sending CampaignStartRelayed: " + 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)
{
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));
}
}
[HarmonyPatch(typeof(SharedCaptainConsoleManager), "OnCampaignStarted")]
[HarmonyPostfix]
[HarmonyWrapSafe]
private static void OnRelayedCampaignStarted(ref SharedCaptainConsoleManager __instance, StartResult startResult, int clientExecutorId)
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!((object)Mainstay<CaptainReconnectManager>.Main).Equals((object?)null) && startResult.Campaign != null && (Object)(object)__instance.CaptainConsoleUI != (Object)null)
{
Log.LogInfo((object)"Relayed Campaign Started. Sending RunStarted event.");
Dictionary<string, string> obj = new Dictionary<string, string> {
{
"campaign",
startResult.Campaign.CampaignId.ToString()
} };
RegionMetadataVo metadata = ((Root)(ref startResult.Campaign.RegionVo)).Metadata;
obj.Add("region", ((RegionMetadataVo)(ref metadata)).Name);
sendEvent(EventType.RunStarted, obj);
}
}
catch (Exception ex)
{
Log.LogError((object)("Error sending relayed RunStarted: " + ex.Message));
}
}
public string GetCompatibleGameVersion()
{
return COMPATIBLE_GAME_VERSION;
}
public string GetVersionCheckUrl()
{
return GAME_VERSION_URL;
}
public BaseUnityPlugin GetPluginObject()
{
return (BaseUnityPlugin)(object)this;
}
}
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.3";
}
}