Decompiled source of GlassPanel v1.8.1

plugins/GlassPanel.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using NuclearOption.Chat;
using NuclearOption.Networking;
using NuclearOption.SavedMission.ObjectiveV2;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GlassPanel")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.8.0.0")]
[assembly: AssemblyInformationalVersion("1.8.0+71ddbeaf6d5dc173f4b567b92a2464a23ce7d1c1")]
[assembly: AssemblyProduct("NO Glass Panel")]
[assembly: AssemblyTitle("GlassPanel")]
[assembly: AssemblyVersion("1.8.0.0")]
namespace GlassPanel;

internal static class ChatBridge
{
	private struct Line
	{
		public string who;

		public string msg;
	}

	private static readonly List<Line> _lines = new List<Line>();

	private static readonly object _lock = new object();

	private const int MAX = 40;

	public static void Add(string who, string msg)
	{
		if (string.IsNullOrEmpty(msg))
		{
			return;
		}
		lock (_lock)
		{
			_lines.Add(new Line
			{
				who = (who ?? ""),
				msg = msg
			});
			if (_lines.Count > 40)
			{
				_lines.RemoveAt(0);
			}
		}
	}

	public static string BuildJson()
	{
		lock (_lock)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < _lines.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append("{\"who\":\"").Append(Esc(_lines[i].who)).Append("\",\"msg\":\"")
					.Append(Esc(_lines[i].msg))
					.Append("\"}");
			}
			return stringBuilder.ToString();
		}
	}

	private static string Esc(string s)
	{
		if (s != null)
		{
			return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
		}
		return "";
	}

	public static void Send(string text, bool allChat)
	{
		try
		{
			if (!string.IsNullOrEmpty(text) && ChatManager.CanSend(text, false, false))
			{
				ChatManager.SendChatMessage(text, allChat);
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch(typeof(ChatManager), "UserCode_TargetReceiveMessage_1307761090")]
internal static class Patch_ReceiveMessage
{
	private static void Postfix(string message, Player player, bool allChat)
	{
		string who = "?";
		try
		{
			if ((Object)(object)player != (Object)null)
			{
				who = player.PlayerName;
			}
		}
		catch
		{
		}
		ChatBridge.Add(who, message);
	}
}
[HarmonyPatch(typeof(ChatManager), "UserCode_RpcServerMessage_1244201393")]
internal static class Patch_ServerMessage
{
	private static void Postfix(string message)
	{
		ChatBridge.Add("SERVER", message);
	}
}
[HarmonyPatch(typeof(WeaponIndicator), "Show")]
internal static class Patch_HideWeaponIndicator
{
	private static void Prefix(ref bool arg)
	{
		if (Plugin.HideWeaponHMD)
		{
			arg = false;
		}
	}
}
internal class MiniServer
{
	private const string WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

	private readonly int _port;

	private readonly byte[] _pageBytes;

	private readonly List<Socket> _clients = new List<Socket>();

	private readonly object _lock = new object();

	private Socket _listen;

	private Thread _acceptThread;

	private volatile bool _running;

	public Action<string> OnMessage;

	public int ClientCount
	{
		get
		{
			lock (_lock)
			{
				return _clients.Count;
			}
		}
	}

	public MiniServer(int port, string pageHtml)
	{
		_port = port;
		_pageBytes = Encoding.UTF8.GetBytes(pageHtml ?? "");
	}

	public void Start()
	{
		try
		{
			_running = true;
			_listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			_listen.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
			_listen.Bind(new IPEndPoint(IPAddress.Any, _port));
			_listen.Listen(16);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("MiniServer LISTENING on " + _listen.LocalEndPoint));
			}
			_acceptThread = new Thread(AcceptLoop)
			{
				IsBackground = true,
				Name = "GlassPanel-accept"
			};
			_acceptThread.Start();
		}
		catch (Exception ex)
		{
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogError((object)("MiniServer bind FAILED: " + ex));
			}
		}
	}

	public void Stop()
	{
		_running = false;
		try
		{
			_listen?.Close();
		}
		catch
		{
		}
		lock (_lock)
		{
			foreach (Socket client in _clients)
			{
				try
				{
					client.Close();
				}
				catch
				{
				}
			}
			_clients.Clear();
		}
	}

	private void AcceptLoop()
	{
		while (_running)
		{
			Socket socket = null;
			try
			{
				socket = _listen.Accept();
			}
			catch
			{
				if (!_running)
				{
					break;
				}
				Thread.Sleep(50);
				continue;
			}
			try
			{
				Handle(socket);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("client handle error: " + ex.Message));
				}
				try
				{
					socket.Close();
				}
				catch
				{
				}
			}
		}
	}

	private void Handle(Socket sock)
	{
		string text;
		using (NetworkStream stream = new NetworkStream(sock, ownsSocket: false))
		{
			text = ReadHeaders(stream);
		}
		if (text == null)
		{
			sock.Close();
		}
		else if (text.IndexOf("upgrade: websocket", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			string text2 = ExtractHeader(text, "sec-websocket-key");
			if (text2 == null)
			{
				sock.Close();
				return;
			}
			string text3;
			using (SHA1 sHA = SHA1.Create())
			{
				text3 = Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(text2 + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
			}
			string s = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + text3 + "\r\n\r\n";
			sock.Send(Encoding.ASCII.GetBytes(s));
			lock (_lock)
			{
				_clients.Add(sock);
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("panel connected (" + ClientCount + " total)"));
			}
			Thread thread = new Thread((ThreadStart)delegate
			{
				ReadLoop(sock);
			});
			thread.IsBackground = true;
			thread.Name = "GlassPanel-read";
			thread.Start();
		}
		else
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("HTTP/1.1 200 OK\r\n");
			stringBuilder.Append("Content-Type: text/html; charset=utf-8\r\n");
			stringBuilder.Append("Content-Length: ").Append(_pageBytes.Length).Append("\r\n");
			stringBuilder.Append("Cache-Control: no-cache\r\nConnection: close\r\n\r\n");
			sock.Send(Encoding.ASCII.GetBytes(stringBuilder.ToString()));
			sock.Send(_pageBytes);
			sock.Close();
		}
	}

	private static string ReadHeaders(NetworkStream stream)
	{
		byte[] array = new byte[4096];
		StringBuilder stringBuilder = new StringBuilder();
		stream.ReadTimeout = 5000;
		while (stringBuilder.Length < 16384)
		{
			int num;
			try
			{
				num = stream.Read(array, 0, array.Length);
			}
			catch
			{
				return null;
			}
			if (num <= 0)
			{
				break;
			}
			stringBuilder.Append(Encoding.ASCII.GetString(array, 0, num));
			if (stringBuilder.ToString().IndexOf("\r\n\r\n", StringComparison.Ordinal) >= 0)
			{
				break;
			}
		}
		if (stringBuilder.Length != 0)
		{
			return stringBuilder.ToString();
		}
		return null;
	}

	private static string ExtractHeader(string request, string name)
	{
		string[] array = request.Split(new string[1] { "\r\n" }, StringSplitOptions.None);
		foreach (string text in array)
		{
			int num = text.IndexOf(':');
			if (num > 0 && text.Substring(0, num).Trim().Equals(name, StringComparison.OrdinalIgnoreCase))
			{
				return text.Substring(num + 1).Trim();
			}
		}
		return null;
	}

	public void Broadcast(string message)
	{
		byte[] buffer = EncodeTextFrame(message);
		lock (_lock)
		{
			for (int num = _clients.Count - 1; num >= 0; num--)
			{
				Socket socket = _clients[num];
				try
				{
					socket.Send(buffer);
				}
				catch
				{
					try
					{
						socket.Close();
					}
					catch
					{
					}
					_clients.RemoveAt(num);
				}
			}
		}
	}

	private void ReadLoop(Socket sock)
	{
		try
		{
			NetworkStream networkStream = new NetworkStream(sock, ownsSocket: false);
			while (_running && sock.Connected)
			{
				int num = networkStream.ReadByte();
				if (num < 0)
				{
					break;
				}
				int num2 = networkStream.ReadByte();
				if (num2 < 0)
				{
					break;
				}
				int num3 = num & 0xF;
				bool flag = (num2 & 0x80) != 0;
				long num4 = num2 & 0x7F;
				switch (num4)
				{
				case 126L:
					num4 = (ReadN(networkStream) << 8) | ReadN(networkStream);
					break;
				case 127L:
				{
					num4 = 0L;
					for (int i = 0; i < 8; i++)
					{
						num4 = (num4 << 8) | ReadN(networkStream);
					}
					break;
				}
				}
				if (num4 < 0 || num4 > 65536)
				{
					break;
				}
				byte[] array = new byte[4];
				if (flag)
				{
					ReadFull(networkStream, array, 4);
				}
				byte[] array2 = new byte[num4];
				ReadFull(networkStream, array2, (int)num4);
				if (flag)
				{
					for (int j = 0; j < num4; j++)
					{
						array2[j] ^= array[j & 3];
					}
				}
				switch (num3)
				{
				case 1:
					if (OnMessage != null)
					{
						OnMessage(Encoding.UTF8.GetString(array2));
					}
					break;
				case 8:
					return;
				}
			}
		}
		catch
		{
		}
		finally
		{
			lock (_lock)
			{
				_clients.Remove(sock);
			}
			try
			{
				sock.Close();
			}
			catch
			{
			}
		}
	}

	private static int ReadN(NetworkStream ns)
	{
		int num = ns.ReadByte();
		if (num < 0)
		{
			throw new IOException();
		}
		return num;
	}

	private static void ReadFull(NetworkStream ns, byte[] buf, int count)
	{
		int num;
		for (int i = 0; i < count; i += num)
		{
			num = ns.Read(buf, i, count - i);
			if (num <= 0)
			{
				throw new IOException();
			}
		}
	}

	private static byte[] EncodeTextFrame(string message)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(message);
		int num = bytes.Length;
		byte[] array = ((num < 126) ? new byte[2]
		{
			129,
			(byte)num
		} : ((num > 65535) ? new byte[10]
		{
			129,
			127,
			0,
			0,
			0,
			0,
			(byte)(num >> 24),
			(byte)(num >> 16),
			(byte)(num >> 8),
			(byte)(num & 0xFF)
		} : new byte[4]
		{
			129,
			126,
			(byte)(num >> 8),
			(byte)(num & 0xFF)
		}));
		byte[] array2 = new byte[array.Length + num];
		Array.Copy(array, array2, array.Length);
		Array.Copy(bytes, 0, array2, array.Length, num);
		return array2;
	}
}
[BepInPlugin("ai.fireballz.noglasspanel", "NO Glass Panel", "1.8.1")]
[BepInProcess("NuclearOption.exe")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "ai.fireballz.noglasspanel";

	internal static ManualLogSource Log;

	internal static bool HideWeaponHMD;

	private MiniServer _server;

	private TelemetryReader _reader;

	private float _accum;

	private float _interval = 1f / 30f;

	private readonly Queue<string> _inbox = new Queue<string>();

	private readonly object _inboxLock = new object();

	private Aircraft _subAircraft;

	private Action<OnRadarWarning> _radarHandler;

	private void Awake()
	{
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		GameObject managerObject = Chainloader.ManagerObject;
		if ((Object)(object)managerObject != (Object)null)
		{
			((Object)managerObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)managerObject);
		}
		int value = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "Port", 8787, "TCP port serving the panel page (HTTP) and the live feed (WebSocket) on the same port.").Value;
		float value2 = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "UpdateHz", 30f, "Telemetry frames per second pushed to connected panels.").Value;
		_interval = 1f / Mathf.Max(1f, value2);
		HideWeaponHMD = ((BaseUnityPlugin)this).Config.Bind<bool>("HMD", "HideWeaponAmmo", false, "Hide the weapon/ammo indicator on the in-game HMD (it lives on the panel instead).").Value;
		_reader = new TelemetryReader();
		_server = new MiniServer(value, LoadPanelHtml());
		_server.OnMessage = OnPanelMessage;
		_server.Start();
		try
		{
			new Harmony("ai.fireballz.noglasspanel").PatchAll();
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("chat receive patch skipped: " + ex.Message));
		}
		Log.LogInfo((object)$"Glass Panel up. On the laptop, open  http://<this-pc-ip>:{value}");
	}

	private void Update()
	{
		if (_server == null)
		{
			return;
		}
		lock (_inboxLock)
		{
			while (_inbox.Count > 0)
			{
				HandlePanelMessage(_inbox.Dequeue());
			}
		}
		EnsureRadarSub();
		_accum += Time.unscaledDeltaTime;
		if (_accum < _interval)
		{
			return;
		}
		_accum = 0f;
		if (_server.ClientCount != 0)
		{
			string text = _reader.BuildFrame();
			if (text != null)
			{
				_server.Broadcast(text);
			}
		}
	}

	private void OnDestroy()
	{
		_server?.Stop();
	}

	private void OnPanelMessage(string msg)
	{
		lock (_inboxLock)
		{
			if (_inbox.Count < 32)
			{
				_inbox.Enqueue(msg);
			}
		}
	}

	private void EnsureRadarSub()
	{
		try
		{
			Aircraft val = default(Aircraft);
			GameManager.GetLocalAircraft(ref val);
			if (val == _subAircraft)
			{
				return;
			}
			if ((Object)(object)_subAircraft != (Object)null && _radarHandler != null)
			{
				_subAircraft.onRadarWarning -= _radarHandler;
			}
			_subAircraft = val;
			if (!((Object)(object)val != (Object)null))
			{
				return;
			}
			if (_radarHandler == null)
			{
				_radarHandler = delegate(OnRadarWarning w)
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					RadarWarnTracker.Mark(w.radar);
				};
			}
			val.onRadarWarning += _radarHandler;
		}
		catch
		{
		}
	}

	private static void HandlePanelMessage(string json)
	{
		if (json == null)
		{
			return;
		}
		if (json.IndexOf("\"cmd\"", StringComparison.Ordinal) >= 0)
		{
			HandleCommand(json);
		}
		else
		{
			if (json.IndexOf("\"chat\"", StringComparison.Ordinal) < 0)
			{
				return;
			}
			bool allChat = json.IndexOf("\"all\":true", StringComparison.Ordinal) >= 0;
			int num = json.IndexOf("\"text\":\"", StringComparison.Ordinal);
			if (num >= 0)
			{
				num += 8;
				int num2 = json.LastIndexOf('"');
				if (num2 > num)
				{
					ChatBridge.Send(json.Substring(num, num2 - num).Replace("\\\"", "\"").Replace("\\\\", "\\"), allChat);
				}
			}
		}
	}

	private static void HandleCommand(string json)
	{
		string text = Extract(json, "\"a\":\"");
		Aircraft val = default(Aircraft);
		if (text == null || !GameManager.GetLocalAircraft(ref val) || (Object)(object)val == (Object)null)
		{
			return;
		}
		try
		{
			switch (text)
			{
			case "gear":
				val.SetGear(!val.gearDeployed);
				break;
			case "wpn+":
				if ((Object)(object)val.weaponManager != (Object)null)
				{
					val.weaponManager.NextWeaponStation();
				}
				break;
			case "wpn-":
				if ((Object)(object)val.weaponManager != (Object)null)
				{
					val.weaponManager.PreviousWeaponStation();
				}
				break;
			case "cm":
				if (val.countermeasureManager != null)
				{
					val.countermeasureManager.PopFlares();
				}
				break;
			}
		}
		catch
		{
		}
	}

	private static string Extract(string json, string marker)
	{
		int num = json.IndexOf(marker, StringComparison.Ordinal);
		if (num < 0)
		{
			return null;
		}
		num += marker.Length;
		int num2 = json.IndexOf('"', num);
		if (num2 <= num)
		{
			return null;
		}
		return json.Substring(num, num2 - num);
	}

	private static string LoadPanelHtml()
	{
		using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("GlassPanel.index.html");
		if (stream == null)
		{
			return "<!doctype html><h1>panel resource missing from mod build</h1>";
		}
		using StreamReader streamReader = new StreamReader(stream);
		return streamReader.ReadToEnd();
	}
}
internal static class RadarWarnTracker
{
	private static readonly Dictionary<Radar, float> _hits = new Dictionary<Radar, float>();

	private static readonly object _lock = new object();

	private const float TTL = 4f;

	public static void Mark(Radar r)
	{
		if ((Object)(object)r == (Object)null)
		{
			return;
		}
		lock (_lock)
		{
			_hits[r] = Time.time;
		}
	}

	public static List<Radar> Active()
	{
		List<Radar> list = new List<Radar>();
		lock (_lock)
		{
			float time = Time.time;
			List<Radar> list2 = null;
			foreach (KeyValuePair<Radar, float> hit in _hits)
			{
				if ((Object)(object)hit.Key == (Object)null || time - hit.Value > 4f)
				{
					(list2 ?? (list2 = new List<Radar>())).Add(hit.Key);
				}
				else
				{
					list.Add(hit.Key);
				}
			}
			if (list2 != null)
			{
				foreach (Radar item in list2)
				{
					_hits.Remove(item);
				}
			}
		}
		return list;
	}
}
internal class TelemetryReader
{
	private const float MS_TO_KN = 1.943844f;

	private const float M_TO_FT = 3.28084f;

	private const float MS_TO_FTMIN = 196.8504f;

	private const float RAD_TO_DEG = 57.29578f;

	public string BuildFrame()
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: 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)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		Aircraft val = default(Aircraft);
		if (!GameManager.GetLocalAircraft(ref val) || (Object)(object)val == (Object)null)
		{
			return null;
		}
		Transform transform = ((Component)val).transform;
		Vector3 val2 = (((Object)(object)val.cockpit != (Object)null && (Object)(object)val.cockpit.rb != (Object)null) ? val.cockpit.rb.velocity : Vector3.zero);
		float speed = ((Unit)val).speed;
		float x = transform.eulerAngles.x;
		float z = transform.eulerAngles.z;
		float v = ((x > 180f) ? (360f - x) : (0f - x));
		float v2 = ((z > 180f) ? (360f - z) : (0f - z));
		float y = transform.eulerAngles.y;
		float v3 = 0f;
		if ((Object)(object)val.cockpit != (Object)null)
		{
			Vector3 val3 = ((Component)val.cockpit).transform.InverseTransformDirection(val2);
			v3 = Mathf.Atan2(val3.y, val3.z) * -57.29578f;
		}
		ControlInputs inputs = val.GetInputs();
		float num = GlobalPositionExtensions.GlobalY(transform.position);
		float num2 = Mathf.Pow(Mathf.Max(0.05f, 1f - 2.25577E-05f * Mathf.Max(0f, num)), 4.2561f);
		List<string> list = new List<string>(28);
		list.Add(Num("tas", speed * 1.943844f));
		list.Add(Num("ias", speed * Mathf.Sqrt(num2) * 1.943844f));
		list.Add(Num("mach", speed / 340f));
		list.Add(Num("alt", GlobalPositionExtensions.GlobalY(transform.position) * 3.28084f));
		list.Add(Num("agl", Mathf.Max(0f, ((Unit)val).radarAlt) * 3.28084f));
		list.Add(Num("vs", val2.y * 196.8504f));
		list.Add(Num("hdg", y));
		list.Add(Num("pitch", v));
		list.Add(Num("roll", v2));
		list.Add(Num("aoa", v3));
		list.Add(Num("g", val.gForce));
		list.Add(Num("engine", EngineThrustKN(val)));
		list.Add(Num("throttle", inputs.throttle));
		list.Add(Bool("gear", val.gearDeployed));
		list.Add(Num("fuelKg", val.GetFuelQuantity()));
		list.Add("\"inputs\":{" + Num("pitch", inputs.pitch) + "," + Num("roll", inputs.roll) + "," + Num("yaw", inputs.yaw) + "," + Num("throttle", inputs.throttle) + "}");
		list.Add("\"weapons\":[" + BuildLoadout(val, out var selIndex) + "]");
		list.Add(Int("weaponIndex", selIndex));
		int flares = 0;
		int chaff = 0;
		int flaresMax = 0;
		int chaffMax = 0;
		ReadCountermeasures(val, ref flares, ref chaff, ref flaresMax, ref chaffMax);
		list.Add(Int("flares", flares));
		list.Add(Int("chaff", chaff));
		list.Add(Int("flaresMax", (flaresMax <= 0) ? 1 : flaresMax));
		list.Add(Int("chaffMax", (chaffMax <= 0) ? 1 : chaffMax));
		list.Add("\"contacts\":[" + BuildContacts(val) + "]");
		list.Add("\"rwr\":[" + BuildRWR(val) + "]");
		list.Add(BuildDamage(val));
		list.Add("\"chat\":[" + ChatBridge.BuildJson() + "]");
		list.Add("\"datalink\":[" + BuildDatalink(val) + "]");
		list.Add(BuildNav(val));
		list.Add("\"objectives\":[" + BuildObjectives() + "]");
		return "{" + string.Join(",", list.ToArray()) + "}";
	}

	private static float EngineThrustKN(Aircraft ac)
	{
		try
		{
			float num = 0f;
			if (ac.engines != null)
			{
				foreach (IEngine engine in ac.engines)
				{
					if (engine != null)
					{
						num += engine.GetThrust();
					}
				}
			}
			return num / 1000f;
		}
		catch
		{
			return 0f;
		}
	}

	private static string BuildLoadout(Aircraft ac, out int selIndex)
	{
		selIndex = 0;
		try
		{
			List<WeaponStation> weaponStations = ((Unit)ac).weaponStations;
			if (weaponStations == null)
			{
				return BuildSelectedWeapon(ac);
			}
			WeaponStation val = (((Object)(object)ac.weaponManager != (Object)null) ? ac.weaponManager.currentWeaponStation : null);
			List<string> list = new List<string>();
			foreach (WeaponStation item in weaponStations)
			{
				if (item != null && !((Object)(object)item.WeaponInfo == (Object)null))
				{
					string text = ((!string.IsNullOrEmpty(item.WeaponInfo.weaponName)) ? item.WeaponInfo.weaponName : item.WeaponInfo.shortName);
					if (val != null && item == val)
					{
						selIndex = list.Count;
					}
					list.Add("{" + Str("name", text ?? "-") + "," + Int("ammo", item.Ammo) + "," + Str("unit", "") + "}");
				}
			}
			return (list.Count > 0) ? string.Join(",", list.ToArray()) : BuildSelectedWeapon(ac);
		}
		catch
		{
			selIndex = 0;
			return BuildSelectedWeapon(ac);
		}
	}

	private static string BuildSelectedWeapon(Aircraft ac)
	{
		WeaponManager weaponManager = ac.weaponManager;
		WeaponStation val = (((Object)(object)weaponManager != (Object)null) ? weaponManager.currentWeaponStation : null);
		if (val == null || (Object)(object)val.WeaponInfo == (Object)null)
		{
			return "";
		}
		string text = ((!string.IsNullOrEmpty(val.WeaponInfo.weaponName)) ? val.WeaponInfo.weaponName : val.WeaponInfo.shortName);
		return "{" + Str("name", text ?? "-") + "," + Int("ammo", val.Ammo) + "," + Str("unit", "RDS") + "}";
	}

	private static void ReadCountermeasures(Aircraft ac, ref int flares, ref int chaff, ref int flaresMax, ref int chaffMax)
	{
		try
		{
			CountermeasureManager countermeasureManager = ac.countermeasureManager;
			if (countermeasureManager == null || !(typeof(CountermeasureManager).GetField("countermeasureStations", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(countermeasureManager) is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					Type type = item.GetType();
					int num = (int)(type.GetField("ammo")?.GetValue(item) ?? ((object)0));
					int num2 = (int)(type.GetField("maxAmmo")?.GetValue(item) ?? ((object)0));
					if (type.GetField("threatTypes")?.GetValue(item) is List<string> list && list.Exists((string x) => x?.ToLower().Contains("radar") ?? false))
					{
						chaff += num;
						chaffMax += num2;
					}
					else
					{
						flares += num;
						flaresMax += num2;
					}
				}
			}
		}
		catch
		{
		}
	}

	private static string BuildContacts(Aircraft ac)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			WeaponManager weaponManager = ac.weaponManager;
			List<Unit> list = (((Object)(object)weaponManager != (Object)null) ? weaponManager.GetTargetList() : null);
			if (list == null)
			{
				return "";
			}
			List<string> list2 = new List<string>();
			int num = 0;
			foreach (Unit item in list)
			{
				if (!((Object)(object)item == (Object)null) && num < 12)
				{
					num++;
					Vector3 val = ((Component)item).transform.position - ((Component)ac).transform.position;
					float magnitude = ((Vector3)(ref val)).magnitude;
					float v = (Mathf.Atan2(val.x, val.z) * 57.29578f + 360f) % 360f;
					list2.Add("{" + Num("brg", v) + "," + Num("rng", magnitude) + "," + Str("type", "H") + "}");
				}
			}
			return string.Join(",", list2.ToArray());
		}
		catch
		{
			return "";
		}
	}

	private static string BuildRWR(Aircraft ac)
	{
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			List<string> list = new List<string>();
			MissileWarning missileWarningSystem = ac.GetMissileWarningSystem();
			if ((Object)(object)missileWarningSystem != (Object)null && missileWarningSystem.knownMissiles != null)
			{
				foreach (Missile knownMissile in missileWarningSystem.knownMissiles)
				{
					if (!((Object)(object)knownMissile == (Object)null))
					{
						Vector3 val = ((Component)knownMissile).transform.position - ((Component)ac).transform.position;
						float v = (Mathf.Atan2(val.x, val.z) * 57.29578f + 360f) % 360f;
						list.Add("{" + Num("brg", v) + "," + Str("band", "M") + "," + Int("lock", 2) + "}");
					}
				}
			}
			foreach (Radar item in RadarWarnTracker.Active())
			{
				if (!((Object)(object)item == (Object)null))
				{
					Vector3 val2 = ((Component)item).transform.position - ((Component)ac).transform.position;
					float v2 = (Mathf.Atan2(val2.x, val2.z) * 57.29578f + 360f) % 360f;
					list.Add("{" + Num("brg", v2) + "," + Str("band", "R") + "," + Int("lock", 1) + "}");
				}
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return "";
		}
	}

	private static string BuildDatalink(Aircraft ac)
	{
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			FactionHQ networkHQ = ((Unit)ac).NetworkHQ;
			if ((Object)(object)networkHQ == (Object)null || networkHQ.trackingDatabase == null)
			{
				return "";
			}
			Faction faction = networkHQ.faction;
			List<string> list = new List<string>();
			int num = 0;
			Unit val = default(Unit);
			foreach (KeyValuePair<PersistentID, TrackingInfo> item in networkHQ.trackingDatabase)
			{
				if (num < 40)
				{
					TrackingInfo value = item.Value;
					if (value != null && value.TryGetUnit(ref val) && !((Object)(object)val == (Object)null) && (object)val != ac)
					{
						Vector3 val2 = ((Component)val).transform.position - ((Component)ac).transform.position;
						float magnitude = ((Vector3)(ref val2)).magnitude;
						float v = (Mathf.Atan2(val2.x, val2.z) * 57.29578f + 360f) % 360f;
						Faction val3 = (((Object)(object)val.NetworkHQ != (Object)null) ? val.NetworkHQ.faction : null);
						string v2 = (((Object)(object)faction != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)faction == (Object)(object)val3) ? "F" : "H");
						list.Add("{" + Num("brg", v) + "," + Num("rng", magnitude) + "," + Str("type", v2) + "}");
						num++;
					}
					continue;
				}
				break;
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return "";
		}
	}

	private static string BuildDamage(Aircraft ac)
	{
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		float v = 1f;
		float v2 = 1f;
		float v3 = 1f;
		float v4 = 1f;
		float v5 = 1f;
		float v6 = 1f;
		string v7 = "";
		string v8 = "";
		try
		{
			if (ac.partDamageTracker != null)
			{
				v = 1f - ac.partDamageTracker.GetDetachedRatio();
			}
			if ((Object)(object)ac.definition != (Object)null)
			{
				v7 = ((UnitDefinition)ac.definition).unitName ?? "";
				v8 = ((UnitDefinition)ac.definition).code ?? "";
			}
			foreach (UnitPart allPart in ((Unit)ac).GetAllParts())
			{
				if (!((Object)(object)allPart == (Object)null) && allPart.IsDetached())
				{
					Vector3 localPosition = ((Component)allPart).transform.localPosition;
					if (localPosition.x < -0.8f)
					{
						v3 = 0f;
					}
					else if (localPosition.x > 0.8f)
					{
						v4 = 0f;
					}
					else if (localPosition.z > 1f)
					{
						v2 = 0f;
					}
					else if (localPosition.z < -1f)
					{
						v5 = 0f;
					}
					else
					{
						v6 = 0.3f;
					}
				}
			}
		}
		catch
		{
		}
		return "\"damage\":{" + Num("hull", v) + "," + Str("name", v7) + "," + Str("code", v8) + ",\"sections\":{" + Num("nose", v2) + "," + Num("lwing", v3) + "," + Num("rwing", v4) + "," + Num("tail", v5) + "," + Num("engine", v6) + "}}";
	}

	private static string BuildNav(Aircraft ac)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			FactionHQ networkHQ = ((Unit)ac).NetworkHQ;
			Airbase val = default(Airbase);
			if ((Object)(object)networkHQ != (Object)null && networkHQ.TryGetNearestAirbase(((Component)ac).transform.position, ref val, default(RunwayQuery)) && (Object)(object)val != (Object)null && (Object)(object)val.center != (Object)null)
			{
				Vector3 val2 = val.center.position - ((Component)ac).transform.position;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				float v = (Mathf.Atan2(val2.x, val2.z) * 57.29578f + 360f) % 360f;
				string networknetworkUniqueName = val.NetworknetworkUniqueName;
				return "\"nav\":{" + Str("name", string.IsNullOrEmpty(networknetworkUniqueName) ? "BASE" : networknetworkUniqueName) + "," + Num("brg", v) + "," + Num("rng", magnitude) + "}";
			}
		}
		catch
		{
		}
		return "\"nav\":null";
	}

	private static string BuildObjectives()
	{
		try
		{
			MissionObjectives objectives = MissionManager.Objectives;
			if (objectives == null || objectives.AllObjectives == null)
			{
				return "";
			}
			List<string> list = new List<string>();
			foreach (Objective allObjective in objectives.AllObjectives)
			{
				if (allObjective == null)
				{
					continue;
				}
				string text = null;
				try
				{
					text = allObjective.ToUIString(false);
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text))
				{
					try
					{
						text = ((object)allObjective).ToString();
					}
					catch
					{
					}
				}
				string v = "";
				try
				{
					v = ((object)Unsafe.As<ObjectiveStatus, ObjectiveStatus>(ref allObjective.Status)/*cast due to .constrained prefix*/).ToString();
				}
				catch
				{
				}
				float v2 = 0f;
				try
				{
					v2 = allObjective.CompletePercent;
				}
				catch
				{
				}
				list.Add("{" + Str("text", text ?? "") + "," + Str("status", v) + "," + Num("pct", v2) + "}");
			}
			return string.Join(",", list.ToArray());
		}
		catch
		{
			return "";
		}
	}

	private static string Num(string k, float v)
	{
		if (float.IsNaN(v) || float.IsInfinity(v))
		{
			v = 0f;
		}
		return "\"" + k + "\":" + v.ToString("0.###", CultureInfo.InvariantCulture);
	}

	private static string Int(string k, int v)
	{
		return "\"" + k + "\":" + v.ToString(CultureInfo.InvariantCulture);
	}

	private static string Bool(string k, bool v)
	{
		return "\"" + k + "\":" + (v ? "true" : "false");
	}

	private static string Str(string k, string v)
	{
		return "\"" + k + "\":\"" + Esc(v) + "\"";
	}

	private static string Esc(string s)
	{
		if (s != null)
		{
			return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
		}
		return "";
	}
}