Decompiled source of OBS Control API v1.0.2

Mods/OBS_Control_API.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Il2CppRUMBLE.Managers;
using Il2CppRUMBLE.Players.Subsystems;
using Il2CppRUMBLE.Utilities;
using MelonLoader;
using Newtonsoft.Json;
using OBS_Control_API;
using RumbleModUI;
using RumbleModdingAPI;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(OBS), "OBS_Control_API", "1.0.2", "Kalamart", null)]
[assembly: VerifyLoaderVersion(0, 6, 5, true)]
[assembly: MelonGame("Buckethead Entertainment", "RUMBLE")]
[assembly: MelonColor(255, 255, 31, 90)]
[assembly: MelonAuthorColor(255, 255, 31, 90)]
[assembly: AssemblyTitle("OBS_Control_API")]
[assembly: AssemblyDescription("Manages a websocket connection to OBS")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OBS_Control_API")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("621d30a5-8fa1-4d87-9826-92c0149b033e")]
[assembly: AssemblyFileVersion("1.0.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace OBS_Control_API;

public static class BuildInfo
{
	public const string ModName = "OBS_Control_API";

	public const string ModVersion = "1.0.2";

	public const string Description = "Manages a websocket connection to OBS";

	public const string Author = "Kalamart";

	public const string Company = "";
}
public class OBS : MelonMod
{
	private class RequestManager
	{
		private ConnectionManager connectionManager;

		private Dictionary<string, RequestResponse> currentRequests;

		public RequestManager()
		{
			currentRequests = new Dictionary<string, RequestResponse>();
		}

		public void SetConnectionManager(ConnectionManager manager)
		{
			connectionManager = manager;
		}

		public string SendRequest(string requestType, object parameters)
		{
			RequestResponse requestResponse = null;
			int num = 0;
			while (requestResponse == null && num < 100)
			{
				string text = NewRequest(requestType, parameters);
				if (text == null)
				{
					return null;
				}
				while (currentRequests[text] == null)
				{
					Thread.Sleep(10);
					num++;
				}
				if (currentRequests[text] == null)
				{
					continue;
				}
				requestResponse = currentRequests[text];
				if (requestResponse.requestStatus.code == 207)
				{
					requestResponse = null;
					Thread.Sleep(10);
					num++;
				}
				else if (!requestResponse.requestStatus.result)
				{
					if (requestResponse.requestStatus.comment == null)
					{
						LogError($"Request {requestType} returned error code {requestResponse.requestStatus.code}");
					}
					else
					{
						LogError($"Request {requestType} returned error code {requestResponse.requestStatus.code}: {requestResponse.requestStatus.comment}");
					}
					return null;
				}
			}
			if (requestResponse == null)
			{
				LogError("Request " + requestType + " timed out");
				return null;
			}
			return requestResponse.responseData;
		}

		private string NewRequest(string requestType, object parameters)
		{
			if (!connectionManager.IsConnected())
			{
				LogError("Request " + requestType + " cannot be executed because the client is not connected to OBS");
				return null;
			}
			string text = Guid.NewGuid().ToString();
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["requestType"] = requestType,
				["requestId"] = text
			};
			if (parameters != null)
			{
				dictionary["requestData"] = parameters;
			}
			Dictionary<string, object> msg = new Dictionary<string, object>
			{
				["op"] = OpCode.Request,
				["d"] = dictionary
			};
			connectionManager.SendMsg(msg);
			currentRequests.Add(text, null);
			return text;
		}

		public void ReceiveResponse(string data_str)
		{
			RequestResponseRaw requestResponseRaw = JsonConvert.DeserializeObject<RequestResponseRaw>(data_str);
			string requestId = requestResponseRaw.requestId;
			if (currentRequests.ContainsKey(requestId))
			{
				RequestResponse requestResponse = new RequestResponse();
				requestResponse.requestStatus = requestResponseRaw.requestStatus;
				if (requestResponseRaw.responseData != null)
				{
					requestResponse.responseData = requestResponseRaw.responseData.ToString();
				}
				else
				{
					requestResponse.responseData = "{}";
				}
				currentRequests[requestId] = requestResponse;
			}
		}
	}

	private class ConnectionManager
	{
		private RequestManager requestManager;

		private ClientWebSocket ws = new ClientWebSocket();

		private string PASSWORD = null;

		private string URL = null;

		private bool authentifying = false;

		private bool waitingOnServer = false;

		private bool shouldReconnect = true;

		public event Action<Event> onEvent;

		public ConnectionManager(RequestManager manager, string OBS_ip, int OBS_port, string OBS_password)
		{
			requestManager = manager;
			manager.SetConnectionManager(this);
			UpdateWebsocketConfig(OBS_ip, OBS_port, OBS_password);
		}

		public void UpdateWebsocketConfig(string OBS_ip, int OBS_port, string OBS_password)
		{
			URL = "ws://" + OBS_ip + ":" + OBS_port;
			PASSWORD = OBS_password;
		}

		public bool IsConnected()
		{
			return ws.State == WebSocketState.Open;
		}

		public void Start()
		{
			Task.Run(() => ConnectAsync());
		}

		private async Task ConnectAsync()
		{
			shouldReconnect = true;
			while (shouldReconnect)
			{
				try
				{
					ws = new ClientWebSocket();
					Uri uri = new Uri(URL);
					await ws.ConnectAsync(uri, CancellationToken.None);
					OnOpen();
					byte[] buffer = new byte[4096];
					while (IsConnected())
					{
						WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
						if (result.MessageType == WebSocketMessageType.Text)
						{
							OnMessage(Encoding.UTF8.GetString(buffer, 0, result.Count));
						}
					}
					OnClose();
				}
				catch (WebSocketException ex2) when (ex2.WebSocketErrorCode == WebSocketError.Faulted)
				{
					if (!waitingOnServer)
					{
						Log("OBS websocket server unavailable, will attempt reconnecting every 3 seconds.");
						waitingOnServer = true;
					}
				}
				catch (WebSocketException wse)
				{
					LogError($"WebSocket error: {wse.WebSocketErrorCode}");
					if (wse.WebSocketErrorCode != WebSocketError.ConnectionClosedPrematurely)
					{
						shouldReconnect = false;
					}
				}
				catch (Exception ex)
				{
					LogError("Error: " + ex.Message);
					shouldReconnect = false;
				}
				if (shouldReconnect)
				{
					await Task.Delay(3000);
				}
			}
		}

		private void OnOpen()
		{
			Log("OBS websocket server available");
			waitingOnServer = false;
		}

		private void OnClose()
		{
			if (authentifying)
			{
				LogError("Authentication failed");
				shouldReconnect = false;
			}
			Log("Connection to OBS closed");
			Task.Run(delegate
			{
				OBS.onDisconnect();
			});
		}

		public async void Stop()
		{
			shouldReconnect = false;
			if (IsConnected())
			{
				Log("Closing websocket connection");
				await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
			}
		}

		private void OnMessage(string payload)
		{
			try
			{
				Msg msg = JsonConvert.DeserializeObject<Msg>(payload);
				if (msg == null)
				{
					return;
				}
				OpCode opCode = (OpCode)Convert.ToInt32(msg.op);
				string text = msg.d.ToString();
				switch (opCode)
				{
				case OpCode.Hello:
				{
					Hello hello = JsonConvert.DeserializeObject<Hello>(text);
					Hello.Authentication authentication = hello.authentication;
					string challenge = authentication.challenge;
					string salt = authentication.salt;
					string text2 = Sha256Base64(PASSWORD + salt);
					string value = Sha256Base64(text2 + challenge);
					Dictionary<string, object> msg2 = new Dictionary<string, object>
					{
						["op"] = OpCode.Identify,
						["d"] = new Dictionary<string, object>
						{
							["rpcVersion"] = 1,
							["authentication"] = value
						}
					};
					authentifying = true;
					SendMsg(msg2);
					break;
				}
				case OpCode.Identified:
					authentifying = false;
					Log("Connection to OBS successful");
					Task.Run(delegate
					{
						OBS.onConnect();
					});
					break;
				case OpCode.Event:
				{
					Event data = JsonConvert.DeserializeObject<Event>(text);
					Task.Run(delegate
					{
						this.onEvent(data);
					});
					break;
				}
				case OpCode.RequestResponse:
					requestManager.ReceiveResponse(text);
					break;
				default:
					LogError($"Received invalid OpCode {opCode}");
					break;
				}
			}
			catch (Exception ex)
			{
				LogError("Error in OnMessage: " + ex.Message);
			}
		}

		private string Sha256Base64(string input)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] inArray = sHA.ComputeHash(Encoding.UTF8.GetBytes(input));
			return Convert.ToBase64String(inArray);
		}

		public async void SendMsg(object msg)
		{
			if (ws.State == WebSocketState.Open)
			{
				byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg));
				ArraySegment<byte> segment = new ArraySegment<byte>(buffer);
				await Task.Run(() => ws.SendAsync(segment, WebSocketMessageType.Text, endOfMessage: true, CancellationToken.None));
			}
		}
	}

	private bool forceReplayBuffer = true;

	private static string ip = "localhost";

	private static int port = 4455;

	private static string password = "your_password_here";

	private string[] keyBindings = new string[2] { "Nothing", "Nothing" };

	private bool[] bindingLocked = new bool[2];

	private bool hapticFeedback = true;

	private Mod Mod = new Mod();

	private static ConnectionManager connectionManager;

	private static RequestManager requestManager;

	private static PlayerHaptics playerHaptics;

	private static bool isReplayBufferActive = false;

	private static bool isRecordingActive = false;

	private static bool isStreamActive = false;

	private static bool stopReplayBufferAtShutdown = false;

	public static event Action onConnect;

	public static event Action onDisconnect;

	public static event Action<Event> onEvent;

	public static event Action<string> onReplayBufferSaved;

	public static event Action onReplayBufferStarted;

	public static event Action onReplayBufferStopped;

	public static event Action<string> onRecordingStarted;

	public static event Action<string> onRecordingStopping;

	public static event Action<string> onRecordingStopped;

	public static event Action onRecordingPaused;

	public static event Action onRecordingResumed;

	public static event Action onStreamStarted;

	public static event Action onStreamStopped;

	public static event Action<string> onRecordFileChanged;

	private static void Log(string msg)
	{
		MelonLogger.Msg(msg);
	}

	private static void LogWarn(string msg)
	{
		MelonLogger.Warning(msg);
	}

	private static void LogError(string msg)
	{
		MelonLogger.Error(msg);
	}

	private void InitClient()
	{
		requestManager = new RequestManager();
		connectionManager = new ConnectionManager(requestManager, ip, port, password);
		InitEvents();
		onConnect += OnConnect;
		onDisconnect += OnDisconnect;
		onRecordingStarted += Feedback;
		onRecordingStopping += Feedback;
		onReplayBufferSaved += Feedback;
	}

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		if (sceneName == "Loader")
		{
			InitClient();
			SetUIOptions();
			OnUISaved();
			UI.instance.UI_Initialized += OnUIInit;
		}
		playerHaptics = ((Component)Singleton<PlayerManager>.instance.playerControllerPrefab).gameObject.GetComponent<PlayerHaptics>();
	}

	public static void Connect()
	{
		if (IsConnected())
		{
			Disconnect();
		}
		connectionManager.UpdateWebsocketConfig(ip, port, password);
		connectionManager.Start();
	}

	public static void Disconnect()
	{
		connectionManager.Stop();
	}

	private void OnConnect()
	{
		SetMainStatus();
		if (!isReplayBufferActive && forceReplayBuffer)
		{
			Log("Replay Buffer was inactive, starting it...");
			stopReplayBufferAtShutdown = true;
			StartReplayBuffer();
		}
	}

	private void OnDisconnect()
	{
		isReplayBufferActive = false;
		isRecordingActive = false;
		isStreamActive = false;
	}

	private void SetUIOptions()
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Expected O, but got Unknown
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Expected O, but got Unknown
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Expected O, but got Unknown
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Expected O, but got Unknown
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Expected O, but got Unknown
		Mod.ModName = "OBS_Control_API";
		Mod.ModVersion = "1.0.2";
		Mod.SetFolder("OBS_Control_API");
		Mod.AddToList("Force enable replay buffer", true, 0, "Never forget to start the replay buffer again!\nThe mod will start it for you on connection, and stop it as you close the game.", new Tags());
		Mod.AddToList("IP address", "localhost", "IP address of the OBS websocket server.", new Tags());
		Mod.AddToList("Port", 4455, "Port used by the OBS websocket server.", new Tags());
		Mod.AddToList("Password", "", "Password for the OBS websocket server.", new Tags());
		Mod.AddToList("Key binding: both left buttons", "Save replay buffer", "Action to perform when both buttons on the left controller are being pressed.\nPossible values:\n- Nothing\n- Save replay buffer\n- Start recording\n- Stop recording\n- Toggle recording", new Tags());
		Mod.AddToList("Key binding: both right buttons", "Nothing", "Action to perform when both buttons on the right controller are being pressed.\nPossible values:\n- Nothing\n- Save replay buffer\n- Start recording\n- Stop recording\n- Toggle recording", new Tags());
		Mod.AddToList("Haptic feedback", true, 0, "Have a haptic impulse on both controllers when the recording state changes.", new Tags());
		Mod.GetFromFile();
	}

	private void OnUIInit()
	{
		Mod.ModSaved += OnUISaved;
		UI.instance.AddMod(Mod);
	}

	private void OnUISaved()
	{
		forceReplayBuffer = (bool)Mod.Settings[0].SavedValue;
		ip = (string)Mod.Settings[1].SavedValue;
		port = (int)Mod.Settings[2].SavedValue;
		password = (string)Mod.Settings[3].SavedValue;
		keyBindings[0] = (string)Mod.Settings[4].SavedValue;
		keyBindings[1] = (string)Mod.Settings[5].SavedValue;
		hapticFeedback = (bool)Mod.Settings[6].SavedValue;
		Connect();
	}

	private void ExecuteKeyBinding(int index)
	{
		bindingLocked[index] = true;
		switch (keyBindings[index])
		{
		case "Save replay buffer":
			if (SaveReplayBuffer())
			{
				Log("Saved replay buffer");
			}
			break;
		case "Start recording":
			if (StartRecord())
			{
				Log("Started recording");
			}
			break;
		case "Stop recording":
		{
			RequestResponse.StopRecord stopRecord = StopRecord();
			if (stopRecord != null)
			{
				Log("Stopped recording, saved to: " + stopRecord.outputPath);
			}
			break;
		}
		case "Toggle recording":
		{
			RequestResponse.ToggleRecord toggleRecord = ToggleRecord();
			if (toggleRecord != null)
			{
				string text = (toggleRecord.outputActive ? "Started" : "Stopped");
				Log(text + " recording");
			}
			break;
		}
		default:
			Log("Invalid key binding \"" + keyBindings[index] + "\"");
			break;
		}
		MelonCoroutines.Start(UnlockKeyBinding(index));
	}

	private IEnumerator UnlockKeyBinding(int index)
	{
		yield return (object)new WaitForSeconds(1f);
		bindingLocked[index] = false;
	}

	private void Feedback()
	{
		if (hapticFeedback)
		{
			HapticFeedback(1f, 0.2f);
		}
	}

	private void Feedback(string value)
	{
		Feedback();
	}

	public static void HapticFeedback(float intensity, float duration)
	{
		if ((Object)(object)playerHaptics != (Object)null)
		{
			playerHaptics.PlayControllerHaptics(intensity, duration, intensity, duration);
		}
	}

	public override void OnFixedUpdate()
	{
		if (!bindingLocked[0] && keyBindings[0] != "Nothing" && LeftController.GetPrimary() > 0f && LeftController.GetSecondary() > 0f)
		{
			Log("Left key binding activated");
			ExecuteKeyBinding(0);
		}
		if (!bindingLocked[1] && keyBindings[1] != "Nothing" && RightController.GetPrimary() > 0f && RightController.GetSecondary() > 0f)
		{
			Log("Right key binding activated");
			ExecuteKeyBinding(1);
		}
	}

	public override void OnApplicationQuit()
	{
		if (connectionManager.IsConnected() && isReplayBufferActive && stopReplayBufferAtShutdown)
		{
			Log("Stopping replay buffer");
			StopReplayBuffer();
		}
		Disconnect();
	}

	public static bool IsConnected()
	{
		return connectionManager.IsConnected();
	}

	public static bool IsReplayBufferActive()
	{
		return isReplayBufferActive;
	}

	public static bool IsRecordingActive()
	{
		return isRecordingActive;
	}

	public static bool IsStreamActive()
	{
		return isStreamActive;
	}

	private void InitEvents()
	{
		connectionManager.onEvent += OnEvent;
	}

	private void OnEvent(Event data)
	{
		Task.Run(delegate
		{
			OBS.onEvent(data);
		});
		string text = data.eventData.ToString();
		if (data.eventType == "ReplayBufferSaved")
		{
			Event.ReplayBufferSaved eventData3 = JsonConvert.DeserializeObject<Event.ReplayBufferSaved>(text);
			Task.Run(delegate
			{
				OBS.onReplayBufferSaved(eventData3.savedReplayPath);
			});
		}
		if (data.eventType == "ReplayBufferStateChanged")
		{
			Event.ReplayBufferStateChanged replayBufferStateChanged = JsonConvert.DeserializeObject<Event.ReplayBufferStateChanged>(text);
			isReplayBufferActive = replayBufferStateChanged.outputActive;
			if (replayBufferStateChanged.outputState == "OBS_WEBSOCKET_OUTPUT_STARTED")
			{
				Task.Run(delegate
				{
					OBS.onReplayBufferStarted();
				});
			}
			else if (replayBufferStateChanged.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPED")
			{
				Task.Run(delegate
				{
					OBS.onReplayBufferStopped();
				});
			}
		}
		else if (data.eventType == "RecordStateChanged")
		{
			Event.RecordStateChanged eventData2 = JsonConvert.DeserializeObject<Event.RecordStateChanged>(text);
			isRecordingActive = eventData2.outputActive;
			if (eventData2.outputState == "OBS_WEBSOCKET_OUTPUT_STARTED")
			{
				Task.Run(delegate
				{
					OBS.onRecordingStarted(eventData2.outputPath);
				});
			}
			else if (eventData2.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPING")
			{
				Task.Run(delegate
				{
					OBS.onRecordingStopping(eventData2.outputPath);
				});
			}
			else if (eventData2.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPED")
			{
				Task.Run(delegate
				{
					OBS.onRecordingStopped(eventData2.outputPath);
				});
			}
			else if (eventData2.outputState == "OBS_WEBSOCKET_OUTPUT_PAUSED")
			{
				Task.Run(delegate
				{
					OBS.onRecordingPaused();
				});
			}
			else if (eventData2.outputState == "OBS_WEBSOCKET_OUTPUT_RESUMED")
			{
				Task.Run(delegate
				{
					OBS.onRecordingResumed();
				});
			}
		}
		else if (data.eventType == "StreamStateChanged")
		{
			Event.ReplayBufferStateChanged replayBufferStateChanged2 = JsonConvert.DeserializeObject<Event.ReplayBufferStateChanged>(text);
			isStreamActive = replayBufferStateChanged2.outputActive;
			if (replayBufferStateChanged2.outputState == "OBS_WEBSOCKET_OUTPUT_STARTED")
			{
				Task.Run(delegate
				{
					OBS.onStreamStarted();
				});
			}
			else if (replayBufferStateChanged2.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPED")
			{
				Task.Run(delegate
				{
					OBS.onStreamStopped();
				});
			}
		}
		else if (data.eventType == "RecordFileChanged")
		{
			Event.RecordFileChanged eventData = JsonConvert.DeserializeObject<Event.RecordFileChanged>(text);
			Task.Run(delegate
			{
				OBS.onRecordFileChanged(eventData.newOutputPath);
			});
		}
	}

	public static string SendRequest(string requestType, object parameters)
	{
		return requestManager.SendRequest(requestType, parameters);
	}

	public static string SendRequest(string requestType)
	{
		return SendRequest(requestType, null);
	}

	public static RequestResponse.GetReplayBufferStatus GetReplayBufferStatus()
	{
		string text = SendRequest("GetReplayBufferStatus");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetReplayBufferStatus>(text);
	}

	public static RequestResponse.GetRecordStatus GetRecordStatus()
	{
		string text = SendRequest("GetRecordStatus");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetRecordStatus>(text);
	}

	public static RequestResponse.GetStreamStatus GetStreamStatus()
	{
		string text = SendRequest("GetStreamStatus");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetStreamStatus>(text);
	}

	public static RequestResponse.GetVersion GetVersion()
	{
		string text = SendRequest("GetVersion");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetVersion>(text);
	}

	public static bool StartStream()
	{
		string text = SendRequest("StartStream");
		return text != null;
	}

	public static bool StopStream()
	{
		string text = SendRequest("StopStream");
		return text != null;
	}

	public static bool StartReplayBuffer()
	{
		string text = SendRequest("StartReplayBuffer");
		return text != null;
	}

	public static bool StopReplayBuffer()
	{
		string text = SendRequest("StopReplayBuffer");
		return text != null;
	}

	public static bool SaveReplayBuffer()
	{
		string text = SendRequest("SaveReplayBuffer");
		return text != null;
	}

	public static RequestResponse.GetLastReplayBufferReplay GetLastReplayBufferReplay()
	{
		string text = SendRequest("GetLastReplayBufferReplay");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetLastReplayBufferReplay>(text);
	}

	public static bool StartRecord()
	{
		string text = SendRequest("StartRecord");
		return text != null;
	}

	public static RequestResponse.StopRecord StopRecord()
	{
		string text = SendRequest("StopRecord");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.StopRecord>(text);
	}

	public static RequestResponse.ToggleRecord ToggleRecord()
	{
		string text = SendRequest("ToggleRecord");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.ToggleRecord>(text);
	}

	public static bool PauseRecord()
	{
		string text = SendRequest("PauseRecord");
		return text != null;
	}

	public static bool ResumeRecord()
	{
		string text = SendRequest("ResumeRecord");
		return text != null;
	}

	public static bool SplitRecordFile()
	{
		string text = SendRequest("SplitRecordFile");
		return text != null;
	}

	public static RequestResponse.GetRecordDirectory GetRecordDirectory()
	{
		string text = SendRequest("GetRecordDirectory");
		if (text == null)
		{
			return null;
		}
		return JsonConvert.DeserializeObject<RequestResponse.GetRecordDirectory>(text);
	}

	public static bool SetRecordDirectory(string recordDirectory)
	{
		Dictionary<string, object> parameters = new Dictionary<string, object> { ["recordDirectory"] = recordDirectory };
		string text = SendRequest("SetRecordDirectory", parameters);
		return text != null;
	}

	private void SetMainStatus()
	{
		try
		{
			isReplayBufferActive = GetReplayBufferStatus().outputActive;
			isRecordingActive = GetRecordStatus().outputActive;
			isStreamActive = GetStreamStatus().outputActive;
		}
		catch (Exception ex)
		{
			MelonLogger.Error(ex.Message);
		}
	}
}
public enum OpCode
{
	Hello = 0,
	Identify = 1,
	Identified = 2,
	Reidentify = 3,
	Event = 5,
	Request = 6,
	RequestResponse = 7,
	RequestBatch = 8,
	RequestBatchResponse = 9
}
public class Msg
{
	public object d { get; set; }

	public int op { get; set; }
}
public class Hello
{
	public class Authentication
	{
		public string challenge { get; set; }

		public string salt { get; set; }
	}

	public Authentication authentication { get; set; }

	public string obsWebSocketVersion { get; set; }

	public string rpcVersion { get; set; }
}
public class RequestResponseRaw
{
	public string requestId { get; set; }

	public RequestResponse.RequestStatus requestStatus { get; set; }

	public object responseData { get; set; }
}
public class RequestResponse
{
	public class RequestStatus
	{
		public bool result { get; set; }

		public int code { get; set; }

		public string comment { get; set; }
	}

	public enum Code
	{
		Success = 100,
		NotReady = 207
	}

	public class GetReplayBufferStatus
	{
		public bool outputActive { get; set; }
	}

	public class ToggleRecord
	{
		public bool outputActive { get; set; }
	}

	public class StopRecord
	{
		public string outputPath { get; set; }
	}

	public class GetRecordStatus
	{
		public bool outputActive { get; set; }

		public bool outputPaused { get; set; }

		public string outputTimecode { get; set; }

		public int outputDuration { get; set; }

		public long outputBytes { get; set; }
	}

	public class GetStreamStatus
	{
		public bool outputActive { get; set; }

		public bool outputReconnecting { get; set; }

		public string outputTimecode { get; set; }

		public int outputDuration { get; set; }

		public float outputCongestion { get; set; }

		public long outputBytes { get; set; }

		public int outputSkippedFrames { get; set; }

		public int outputTotalFrames { get; set; }
	}

	public class GetVersion
	{
		public string obsVersion { get; set; }

		public string obsWebSocketVersion { get; set; }

		public int rpcVersion { get; set; }

		public string[] availableRequests { get; set; }

		public string[] supportedImageFormats { get; set; }

		public string platform { get; set; }

		public string platformDescription { get; set; }
	}

	public class GetLastReplayBufferReplay
	{
		public string savedReplayPath { get; set; }
	}

	public class GetRecordDirectory
	{
		public string recordDirectory { get; set; }
	}

	public RequestStatus requestStatus { get; set; }

	public string responseData { get; set; }
}
public class Event
{
	public class ReplayBufferStateChanged
	{
		public bool outputActive { get; set; }

		public string outputState { get; set; }
	}

	public class ReplayBufferSaved
	{
		public string savedReplayPath { get; set; }
	}

	public class RecordStateChanged
	{
		public bool outputActive { get; set; }

		public string outputPath { get; set; }

		public string outputState { get; set; }
	}

	public class RecordFileChanged
	{
		public string newOutputPath { get; set; }
	}

	public object eventData { get; set; }

	public int eventIntent { get; set; }

	public string eventType { get; set; }
}