using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using MoCore;
using Newtonsoft.Json;
using RegionVo;
using SlipInfo.Data;
using SlipInfo.Handlers;
using SlipInfo.Responses;
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.slipinfo")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Local API for getting game state information")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4+82702a024f645c93c34f4c29e3e58babb0f5940c")]
[assembly: AssemblyProduct("SlipInfo")]
[assembly: AssemblyTitle("com.mosadie.slipinfo")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.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 SlipInfo
{
[BepInPlugin("com.mosadie.slipinfo", "SlipInfo", "1.0.4")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInProcess("Slipstream_Win.exe")]
public class Plugin : BaseUnityPlugin, MoPlugin
{
private static ConfigEntry<int> port;
private static ConfigEntry<string> prefix;
private static ConfigEntry<bool> debugLogs;
private static HttpListener listener;
internal static ManualLogSource Log;
private Dictionary<string, InfoHandler> handlers;
public static readonly string COMPATIBLE_GAME_VERSION = "4.1579";
public static readonly string GAME_VERSION_URL = "https://raw.githubusercontent.com/MoSadie/SlipInfo/refs/heads/main/versions.json";
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;
}
port = ((BaseUnityPlugin)this).Config.Bind<int>("Server Settings", "Port", 8001, "Port to listen on.");
prefix = ((BaseUnityPlugin)this).Config.Bind<string>("Server Settings", "Prefix", "slipinfo", "Prefix to have in path. Ex http://localhost:<port>/<prefix>/version");
debugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogs", false, "Enable additional logging for debugging");
if (!HttpListener.IsSupported)
{
Log.LogError((object)"HttpListener is not supported on this platform.");
listener = null;
return;
}
listener = new HttpListener();
listener.Prefixes.Add($"http://127.0.0.1:{port.Value}/{prefix.Value}/");
listener.Prefixes.Add($"http://localhost:{port.Value}/{prefix.Value}/");
handlers = new Dictionary<string, InfoHandler>();
addHandler(new VersionHandler());
addHandler(new CrewListHandler());
addHandler(new CrewSearchHandler());
addHandler(new CrewSelfHandler());
addHandler(new ShipInfoHandler());
addHandler(new EnemyShipInfoHandler());
addHandler(new RunInfoHandler());
listener.Start();
listener.BeginGetContext(HandleRequest, listener);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin com.mosadie.slipinfo is loaded!");
Application.quitting += ApplicationQuitting;
}
catch (PlatformNotSupportedException ex)
{
Log.LogError((object)"HttpListener is not supported on this platform.");
Log.LogError((object)ex.Message);
}
catch (Exception ex2)
{
Log.LogError((object)"An error occurred while starting the plugin.");
Log.LogError((object)ex2.Message);
}
}
internal static void debugLogInfo(string message)
{
if (debugLogs.Value)
{
Log.LogInfo((object)message);
}
}
internal static void debugLogWarn(string message)
{
if (debugLogs.Value)
{
Log.LogWarning((object)message);
}
}
internal static void debugLogError(string message)
{
if (debugLogs.Value)
{
Log.LogError((object)message);
}
}
internal static void debugLogDebug(string message)
{
if (debugLogs.Value)
{
Log.LogDebug((object)message);
}
}
private void addHandler(InfoHandler handler)
{
if (handlers != null && handler != null)
{
string text = "/" + prefix.Value + "/" + handler.GetPath();
if (handlers.ContainsKey(text))
{
Log.LogWarning((object)("Duplicate path attempted to register! " + text));
return;
}
Log.LogInfo((object)("Registered handler for " + text));
handlers.Add(text, handler);
}
}
private void HandleRequest(IAsyncResult result)
{
debugLogInfo("Handling request");
try
{
HttpListener httpListener = (HttpListener)result.AsyncState;
HttpListenerContext httpListenerContext = httpListener.EndGetContext(result);
HttpListenerRequest request = httpListenerContext.Request;
HttpListenerResponse response = httpListenerContext.Response;
string text = request.RawUrl.Split('?', 2)[0];
HttpStatusCode statusCode;
string s;
if (handlers.ContainsKey(text))
{
debugLogInfo("Handling request with path: " + text);
InfoResponse infoResponse = handlers[text].HandleRequest(request.QueryString);
statusCode = infoResponse.status;
s = infoResponse.response;
}
else
{
debugLogInfo("No handler found.");
statusCode = HttpStatusCode.BadRequest;
s = "{\"error\": \"Bad Request\"}";
}
response.StatusCode = (int)statusCode;
response.Headers.Add("Access-Control-Allow-Origin", "*");
byte[] bytes = Encoding.UTF8.GetBytes(s);
response.ContentLength64 = bytes.Length;
Stream outputStream = response.OutputStream;
outputStream.Write(bytes, 0, bytes.Length);
outputStream.Close();
httpListener.BeginGetContext(HandleRequest, httpListener);
}
catch (Exception ex)
{
Log.LogError((object)"An error occurred while handling the request.");
Log.LogError((object)ex.Message);
Log.LogError((object)ex.StackTrace);
}
}
private void ApplicationQuitting()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Stopping server");
listener.Close();
}
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.slipinfo";
public const string PLUGIN_NAME = "SlipInfo";
public const string PLUGIN_VERSION = "1.0.4";
}
}
namespace SlipInfo.Responses
{
internal class CrewCrewmateResponse
{
public CrewmateInfo crewmate;
public CrewCrewmateResponse(Crewmate crewmateIn)
{
if ((Object)(object)crewmateIn == (Object)null)
{
crewmate = null;
}
else
{
crewmate = new CrewmateInfo(crewmateIn);
}
}
}
public class CrewListResponse
{
public List<CrewmateInfo> crewList;
public CrewListResponse(List<Crewmate> crewList)
{
this.crewList = new List<CrewmateInfo>();
if (crewList == null)
{
return;
}
foreach (Crewmate crew in crewList)
{
this.crewList.Add(new CrewmateInfo(crew));
}
}
}
internal class CrewResponse
{
public CrewmateInfo crewmate;
public CrewResponse(string username)
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
crewmate = null;
return;
}
MpCrewController crew = val.Crew;
if (crew == null)
{
crewmate = null;
return;
}
foreach (Crewmate item in crew.AllCrew())
{
try
{
if (item.Client.Player.DisplayName.Equals(username, StringComparison.OrdinalIgnoreCase))
{
crewmate = new CrewmateInfo(item);
break;
}
}
catch (Exception ex)
{
Plugin.debugLogDebug("An error occurred while checking crewmate display names in CrewResponse: " + ex.Message);
}
}
}
public CrewResponse(Crewmate crewmateIn)
{
if ((Object)(object)crewmateIn == (Object)null)
{
crewmate = null;
}
else
{
crewmate = new CrewmateInfo(crewmateIn);
}
}
}
internal class EnemyShipResponse
{
public EnemyShipInfo enemyShip;
public EnemyShipResponse(MpShipController ship, MpScenarioController scenario)
{
if (ship == null || scenario == null || scenario.CurrentScenario.Battle == null)
{
enemyShip = null;
}
else
{
enemyShip = new EnemyShipInfo(ship, scenario);
}
}
}
internal class ShipResponse
{
private ShipInfo ship;
public ShipResponse(MpShipController ship)
{
if (ship == null)
{
this.ship = null;
}
else
{
this.ship = new ShipInfo(ship);
}
}
}
internal class VersionResponse
{
public string version;
public VersionResponse(Version version)
{
this.version = version.ToString();
}
}
}
namespace SlipInfo.Handlers
{
internal class CrewListHandler : InfoHandler
{
public string GetPath()
{
return "getCrew";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
Plugin.Log.LogError((object)"An error occurred in CrewListHandler! mpSvc was null!");
return new InfoResponse("{\"error\": \"An error occurred in CrewListHandler! mpSvc was null!\"}", HttpStatusCode.InternalServerError);
}
if (val.Crew == null)
{
Plugin.Log.LogError((object)"An error occurred in CrewListHandler! MpCrewController was null!");
return new InfoResponse("{\"error\": \"An error occurred in CrewListHandler: MpCrewController was null!\"}", HttpStatusCode.InternalServerError);
}
CrewListResponse crewListResponse = new CrewListResponse(val.Crew.CrewmatesOnBoard);
try
{
string response = JsonConvert.SerializeObject((object)crewListResponse);
Plugin.debugLogInfo("Returning crew list.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An error occurred in CrewListHandler: " + ex.Message));
return new InfoResponse("{\"error\": \"An error occured in CrewListHandler: " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal class CrewSearchHandler : InfoHandler
{
public string GetPath()
{
return "getCrewByUsername";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
if (query == null || query.Get("username") == null)
{
Plugin.debugLogError("Invalid query! (CrewSearchHandler)");
return new InfoResponse("{\"error\": \"Invalid query!\"}", HttpStatusCode.BadRequest);
}
CrewResponse crewResponse = new CrewResponse(query.Get("username"));
if (crewResponse.crewmate == null)
{
Plugin.debugLogError("User not found! (CrewSearchHandler)");
return new InfoResponse("{\"error\": \"User not found!\"}", HttpStatusCode.NotFound);
}
try
{
string response = JsonConvert.SerializeObject((object)crewResponse);
Plugin.debugLogInfo("Returning crewmate info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An error occurred in CrewSearchHandler: " + ex.Message));
return new InfoResponse("{\"error\": \"An error occured in CrewSearchHandler: " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal class CrewSelfHandler : InfoHandler
{
public string GetPath()
{
return "getSelf";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
Plugin.Log.LogError((object)"An error occurred handling self crew. null MpSvc.");
return new InfoResponse("{\"error\": \"An error occurred handling self crew. null MpSvc.\"}", HttpStatusCode.InternalServerError);
}
MpCrewController crew = val.Crew;
if (crew == null)
{
Plugin.Log.LogError((object)"An error occurred handling self crew. null MpCrewController.");
return new InfoResponse("{\"error\": \"An error occurred handling self crew. null MpCrewController.\"}", HttpStatusCode.InternalServerError);
}
Crewmate val2 = null;
foreach (Crewmate item in crew.CrewmatesOnBoard)
{
if (item.IsLocalPlayer)
{
val2 = item;
break;
}
}
if ((Object)(object)val2 == (Object)null)
{
Plugin.debugLogError("Could not find the local crewmember. (CrewSelfHandler)");
return new InfoResponse("{\"error\": \"Could not find the local crewmember.\"}", HttpStatusCode.InternalServerError);
}
try
{
string response = JsonConvert.SerializeObject((object)new CrewResponse(val2));
Plugin.debugLogInfo("Returning self crewmate info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An exception occurred handling self crew. " + ex.Message));
return new InfoResponse("{\"error\": \"An exception occurred handling self crew. " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal class EnemyShipInfoHandler : InfoHandler
{
public string GetPath()
{
return "getEnemyShipInfo";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
try
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
return new InfoResponse("{\"error\": \"An error occurred handling getting enemy ship info. null MpSvc.\"}", HttpStatusCode.InternalServerError);
}
MpShipController ships = val.Ships;
MpScenarioController scenarios = val.Scenarios;
if (ships == null)
{
return new InfoResponse("{\"error\": \"An error occurred handling getting enemy ship info. null MpShipController.\"}", HttpStatusCode.InternalServerError);
}
if (scenarios == null)
{
return new InfoResponse("{\"error\": \"An error occurred handling getting enemy ship info. null MpScenarioController.\"}", HttpStatusCode.InternalServerError);
}
string response = JsonConvert.SerializeObject((object)new EnemyShipResponse(ships, scenarios));
Plugin.debugLogInfo("Returning enemy ship info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An exception occurred handling getting enemy ship info. " + ex.Message));
return new InfoResponse("{\"error\": \"An exception occurred handling getting enemy ship info. " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal interface InfoHandler
{
InfoResponse HandleRequest(NameValueCollection query);
string GetPath();
}
public class InfoResponse
{
public string response;
public HttpStatusCode status;
public InfoResponse(string response, HttpStatusCode status)
{
this.response = response;
this.status = status;
}
}
internal class RunInfoHandler : InfoHandler
{
public string GetPath()
{
return "getRunInfo";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
try
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
Plugin.Log.LogError((object)"An error occurred in RunInfoHandler! mpSvc was null!");
return new InfoResponse("{\"error\": \"An error occurred in RunInfoHandler! mpSvc was null!\"}", HttpStatusCode.InternalServerError);
}
if (val.Campaigns == null)
{
Plugin.Log.LogError((object)"An error occurred in RunInfoHandler! MpCampaignController was null!");
return new InfoResponse("{\"error\": \"An error occurred in RunInfoHandler: MpCampaignController was null!\"}", HttpStatusCode.InternalServerError);
}
string response = JsonConvert.SerializeObject((object)new RunInfo(val.Campaigns));
Plugin.debugLogInfo("Returning run info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An exception occurred handling getting run info. " + ex.Message));
return new InfoResponse("{\"error\": \"An exception occurred handling getting run info. " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal class ShipInfoHandler : InfoHandler
{
public string GetPath()
{
return "getShipInfo";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
try
{
MpSvc val = Svc.Get<MpSvc>();
if (val == null)
{
Plugin.Log.LogError((object)"An error occurred in ShipInfoHandler! mpSvc was null!");
return new InfoResponse("{\"error\": \"An error occurred handling ship info. null MpSvc.\"}", HttpStatusCode.InternalServerError);
}
MpShipController ships = val.Ships;
if (ships == null)
{
Plugin.Log.LogError((object)"An error occurred in ShipInfoHandler! MpShipController was null!");
return new InfoResponse("{\"error\": \"An error occurred handling ship info. null MpShipController.\"}", HttpStatusCode.InternalServerError);
}
string response = JsonConvert.SerializeObject((object)new ShipInfo(ships));
Plugin.debugLogInfo("Returning ship info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An exception occurred handling ship info. " + ex.Message));
return new InfoResponse("{\"error\": \"An exception occurred handling ship info. " + ex.Message + "\"}", HttpStatusCode.InternalServerError);
}
}
}
internal class VersionHandler : InfoHandler
{
public string GetPath()
{
return "version";
}
public InfoResponse HandleRequest(NameValueCollection query)
{
string response = JsonConvert.SerializeObject((object)new VersionResponse(typeof(VersionHandler).Assembly.GetName().Version));
Plugin.debugLogInfo("Returning SlipInfo version info.");
return new InfoResponse(response, HttpStatusCode.OK);
}
}
}
namespace SlipInfo.Data
{
public class CrewmateInfo
{
public string name;
public string archetype;
public string skin;
public int level;
public int xp;
public float currentHealth;
public float maxHealth;
public float currentShields;
public float maxShields;
public bool isCaptain;
public bool isLocalPlayer;
public CrewmateInfo(Crewmate crewmate)
{
name = ((crewmate.Client != null) ? crewmate.Client.Player.DisplayName : "Crew");
archetype = crewmate.ArchetypeId;
skin = crewmate.SkinId;
level = ((crewmate.Progression != null) ? crewmate.Progression.Level : (-1));
xp = ((crewmate.Progression != null) ? crewmate.Progression.TotalXp : (-1));
currentHealth = crewmate.Health;
maxHealth = ((crewmate.Stats != null) ? crewmate.Stats.MaxHealth : (-1));
currentShields = crewmate.Shields;
maxShields = ((crewmate.Stats != null) ? crewmate.Stats.MaxShields : (-1));
isCaptain = getIsCaptain(crewmate);
isLocalPlayer = crewmate.IsLocalPlayer;
}
private bool getIsCaptain(Crewmate crewmate)
{
try
{
MpCaptainController captains = Svc.Get<MpSvc>().Captains;
if (captains == null || captains.CaptainClient == null)
{
return false;
}
return ((object)crewmate.Client).Equals((object?)captains.CaptainClient);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("An error occurred while checking if the crewmate is the captain: " + ex.Message));
return false;
}
}
}
internal class EnemyShipInfo
{
public float maxHealth;
public float minHealth;
public float currentHealth;
public string name;
public string invaders;
public string intel;
public uint threatLevel;
public uint cargoLevel;
public uint speedLevel;
public EnemyShipInfo(MpShipController ship, MpScenarioController scenario)
{
if (ship != null && scenario != null && scenario.CurrentScenario.Battle != null)
{
Plugin.debugLogInfo("EnemyShipInfo start");
maxHealth = ship.EnemyShipHealth.Max;
minHealth = ship.EnemyShipHealth.Min;
currentHealth = ship.EnemyShipHealth.Current;
Plugin.debugLogInfo($"enemy1 {maxHealth} {minHealth} {currentHealth}");
BattleScenarioVo battle = scenario.CurrentScenario.Battle;
name = battle.Metadata.EnemyName;
invaders = battle.Metadata.InvaderDescription;
intel = battle.Metadata.IntelDescription;
Plugin.debugLogInfo("enemy2 " + name + " : " + invaders + " : " + intel);
threatLevel = battle.Metadata.ThreatLevel;
cargoLevel = battle.Metadata.CargoLevel;
speedLevel = battle.Metadata.SpeedLevel;
Plugin.debugLogInfo($"enemy3 {threatLevel} {cargoLevel} {speedLevel}");
}
else
{
maxHealth = 0f;
minHealth = 0f;
currentHealth = 0f;
name = "";
invaders = "";
intel = "";
threatLevel = 0u;
cargoLevel = 0u;
speedLevel = 0u;
}
}
}
internal class RunInfo
{
public string region;
public string regionDescription;
public string sector;
public int runId;
public RunInfo(MpCampaignController mpCampaignController)
{
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
if (mpCampaignController == null)
{
region = null;
regionDescription = null;
sector = null;
runId = -1;
return;
}
if (!mpCampaignController.IsCampaignInProgress)
{
region = "Space";
regionDescription = "The vast expanse of space. Perfect place to plan the next adventure!";
sector = "The Void";
runId = -1;
return;
}
if (mpCampaignController.CurrentCampaign == null)
{
Plugin.debugLogInfo("Current campaign is null");
return;
}
if (mpCampaignController.CurrentCampaign.CaptainCampaign != null)
{
Root regionVo = mpCampaignController.CurrentCampaign.CaptainCampaign.RegionVo;
RegionMetadataVo metadata = ((Root)(ref regionVo)).Metadata;
region = ((RegionMetadataVo)(ref metadata)).Name;
regionVo = mpCampaignController.CurrentCampaign.CaptainCampaign.RegionVo;
metadata = ((Root)(ref regionVo)).Metadata;
regionDescription = ((RegionMetadataVo)(ref metadata)).Description;
sector = ((SectorDefVo)(ref mpCampaignController.CurrentCampaign.CaptainCampaign.CurrentSectorVo.Definition)).Name;
}
runId = mpCampaignController.CurrentCampaign.CampaignId;
}
}
internal class ShipInfo
{
public float maxHealth;
public float minHealth;
public float currentHealth;
public int maxFuel;
public int currentFuel;
public int currentSalvage;
public int currentGems;
public ShipInfo(MpShipController ship)
{
if (ship != null)
{
maxHealth = ship.CaptainShipHealth.Max;
minHealth = ship.CaptainShipHealth.Min;
currentHealth = ship.CaptainShipHealth.Current;
maxFuel = ship.CaptainFuelTank.CurrentCapacity;
currentFuel = ship.CaptainFuelTank.CurrentAmount;
currentSalvage = ship.CaptainShipInventory.GetInventoryAmount((ShipItemType)0);
currentGems = ship.CaptainShipInventory.GetInventoryAmount((ShipItemType)1);
}
}
}
}