using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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 Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppRUMBLE.Managers;
using Il2CppRUMBLE.Players.Subsystems;
using Il2CppRUMBLE.Utilities;
using MelonLoader;
using Microsoft.CodeAnalysis;
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.1.0", "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: AssemblyDescription("Manages a websocket connection to OBS")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("621d30a5-8fa1-4d87-9826-92c0149b033e")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("OBS_Control_API")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+e46140531f4335a1e36e0d6a6251bc46257daaf5")]
[assembly: AssemblyProduct("OBS_Control_API")]
[assembly: AssemblyTitle("OBS_Control_API")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
}
namespace OBS_Control_API
{
public class OBS : MelonMod
{
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 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 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 float hapticsDuration = 1f;
private bool enableSFX = true;
private Mod Mod = new Mod();
private static ConnectionManager connectionManager = null;
private static RequestManager requestManager = null;
private static PlayerHaptics playerHaptics = null;
private static bool isReplayBufferActive = false;
private static bool isRecordingActive = false;
private static bool isStreamActive = false;
private static bool isWindows = true;
private static string recordingDirectory = "";
private static string sceneUuid = "";
private static bool stopReplayBufferAtShutdown = false;
private static GameObject OBS_SFX_Players = null;
private static GameObject screenshotSFXPlayer = null;
private static GameObject confirmationSFXPlayer = null;
private static GameObject startRecordingSFXPlayer = null;
private static GameObject stopRecordingSFXPlayer = null;
private static Il2CppAssetBundle bundle;
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;
public static event Action<string> onScreenshotSaved;
private void InitEvents()
{
connectionManager.onEvent += OnEvent;
}
private void OnEvent(Event data)
{
Event data2 = data;
Task.Run(delegate
{
OBS.onEvent(data2);
});
string text = data2.eventData.ToString();
if (data2.eventType == "ReplayBufferSaved")
{
Event.ReplayBufferSaved eventData4 = JsonConvert.DeserializeObject<Event.ReplayBufferSaved>(text);
Task.Run(delegate
{
OBS.onReplayBufferSaved(eventData4.savedReplayPath);
});
}
if (data2.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 (data2.eventType == "RecordStateChanged")
{
Event.RecordStateChanged eventData3 = JsonConvert.DeserializeObject<Event.RecordStateChanged>(text);
isRecordingActive = eventData3.outputActive;
if (eventData3.outputState == "OBS_WEBSOCKET_OUTPUT_STARTED")
{
Task.Run(delegate
{
OBS.onRecordingStarted(eventData3.outputPath);
});
}
else if (eventData3.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPING")
{
Task.Run(delegate
{
OBS.onRecordingStopping(eventData3.outputPath);
});
}
else if (eventData3.outputState == "OBS_WEBSOCKET_OUTPUT_STOPPED")
{
Task.Run(delegate
{
OBS.onRecordingStopped(eventData3.outputPath);
});
}
else if (eventData3.outputState == "OBS_WEBSOCKET_OUTPUT_PAUSED")
{
Task.Run(delegate
{
OBS.onRecordingPaused();
});
}
else if (eventData3.outputState == "OBS_WEBSOCKET_OUTPUT_RESUMED")
{
Task.Run(delegate
{
OBS.onRecordingResumed();
});
}
}
else if (data2.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 (data2.eventType == "RecordFileChanged")
{
Event.RecordFileChanged eventData2 = JsonConvert.DeserializeObject<Event.RecordFileChanged>(text);
Task.Run(delegate
{
OBS.onRecordFileChanged(eventData2.newOutputPath);
});
}
else if (data2.eventType == "ScreenshotSaved")
{
Event.ScreenshotSaved eventData = JsonConvert.DeserializeObject<Event.ScreenshotSaved>(text);
Task.Run(delegate
{
OBS.onScreenshotSaved(eventData.savedScreenshotPath);
});
}
}
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;
}
private GameObject createAudioPlayer(AudioClip clip, string objectName, float volume)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Expected O, but got Unknown
GameObject val = new GameObject(objectName);
val.transform.SetParent(OBS_SFX_Players.transform);
AudioSource val2 = val.AddComponent<AudioSource>();
val2.clip = clip;
val2.volume = volume;
return val;
}
private void LoadAssets()
{
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
using Stream stream = ((MelonBase)this).MelonAssembly.Assembly.GetManifestResourceStream("OBS_Control_API.Resources.obs_sfx");
byte[] array = new byte[stream.Length];
stream.Read(array, 0, array.Length);
bundle = Il2CppAssetBundleManager.LoadFromMemory(Il2CppStructArray<byte>.op_Implicit(array));
Log($"Reading assets in bundle {bundle}");
AudioClip clip = bundle.LoadAsset<AudioClip>("screenshot");
AudioClip clip2 = bundle.LoadAsset<AudioClip>("confirmation");
AudioClip clip3 = bundle.LoadAsset<AudioClip>("start_recording");
AudioClip clip4 = bundle.LoadAsset<AudioClip>("stop_recording");
Log("Finished loading assets");
OBS_SFX_Players = new GameObject("OBS_SFX_Players");
Object.DontDestroyOnLoad((Object)(object)OBS_SFX_Players);
screenshotSFXPlayer = createAudioPlayer(clip, "screenshotSFXPlayer", 1f);
confirmationSFXPlayer = createAudioPlayer(clip2, "confirmationSFXPlayer", 0.6f);
startRecordingSFXPlayer = createAudioPlayer(clip3, "startRecordingSFXPlayer", 0.2f);
stopRecordingSFXPlayer = createAudioPlayer(clip4, "stopRecordingSFXPlayer", 0.2f);
}
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
{
playerHaptics = ((Component)Singleton<PlayerManager>.instance.playerControllerPrefab).gameObject.GetComponent<PlayerHaptics>();
if (sceneName == "Loader")
{
LoadAssets();
InitClient();
SetUIOptions();
OnUISaved();
UI.instance.UI_Initialized += OnUIInit;
}
}
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_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Expected O, but got Unknown
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Expected O, but got Unknown
Mod.ModName = "OBS_Control_API";
Mod.ModVersion = "1.1.0";
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\n- Save screenshot", 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\n- Save screenshot", new Tags());
Mod.AddToList("Haptic feedback duration", 0.2f, "Duration of the haptic impulse when an action is successful (set to 0 to disable).", new Tags());
Mod.AddToList("Audio feedback", true, 0, "Set to true to get a sound effect when an action is successful", 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;
hapticsDuration = (float)Mod.Settings[6].SavedValue;
enableSFX = (bool)Mod.Settings[7].SavedValue;
Connect();
}
private void ExecuteKeyBinding(int index)
{
bindingLocked[index] = true;
bool flag = false;
GameObject audioPlayer = null;
switch (keyBindings[index])
{
case "Save replay buffer":
if (SaveReplayBuffer())
{
Log("Saved replay buffer");
audioPlayer = confirmationSFXPlayer;
flag = true;
}
break;
case "Start recording":
if (StartRecord())
{
Log("Started recording");
audioPlayer = startRecordingSFXPlayer;
flag = true;
}
break;
case "Stop recording":
{
RequestResponse.StopRecord stopRecord = StopRecord();
if (stopRecord != null)
{
Log("Stopped recording, saved to: " + stopRecord.outputPath);
audioPlayer = stopRecordingSFXPlayer;
flag = true;
}
break;
}
case "Toggle recording":
{
RequestResponse.ToggleRecord toggleRecord = ToggleRecord();
if (toggleRecord != null)
{
bool outputActive = toggleRecord.outputActive;
string text = (outputActive ? "Started" : "Stopped");
Log(text + " recording");
audioPlayer = (outputActive ? startRecordingSFXPlayer : stopRecordingSFXPlayer);
flag = true;
}
break;
}
case "Save screenshot":
if (SaveSourceScreenshot())
{
Log("Saved screenshot");
audioPlayer = screenshotSFXPlayer;
flag = true;
}
break;
default:
Log("Invalid key binding \"" + keyBindings[index] + "\"");
break;
}
if (flag)
{
Feedback(audioPlayer);
}
MelonCoroutines.Start(UnlockKeyBinding(index));
}
private IEnumerator UnlockKeyBinding(int index)
{
yield return (object)new WaitForSeconds(1f);
bindingLocked[index] = false;
}
private void Feedback(GameObject audioPlayer)
{
if (enableSFX && audioPlayer != null)
{
audioPlayer.GetComponent<AudioSource>().Play();
}
if (hapticsDuration > 0f)
{
HapticFeedback(1f, hapticsDuration);
}
}
public static void playConfirmationSFX()
{
confirmationSFXPlayer.GetComponent<AudioSource>().Play();
}
public static void playScreenshotSFX()
{
screenshotSFXPlayer.GetComponent<AudioSource>().Play();
}
public static void playStartRecordingSFX()
{
startRecordingSFXPlayer.GetComponent<AudioSource>().Play();
}
public static void playStopRecordingSFX()
{
stopRecordingSFXPlayer.GetComponent<AudioSource>().Play();
}
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");
new Thread((ThreadStart)delegate
{
ExecuteKeyBinding(0);
}).Start();
}
if (!bindingLocked[1] && keyBindings[1] != "Nothing" && RightController.GetPrimary() > 0f && RightController.GetSecondary() > 0f)
{
Log("Right key binding activated");
new Thread((ThreadStart)delegate
{
ExecuteKeyBinding(1);
}).Start();
}
}
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;
}
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;
}
public static RequestResponse.GetCurrentProgramScene GetCurrentProgramScene()
{
string text = SendRequest("GetCurrentProgramScene");
if (text == null)
{
return null;
}
return JsonConvert.DeserializeObject<RequestResponse.GetCurrentProgramScene>(text);
}
public static bool SaveSourceScreenshot()
{
string text = (isWindows ? "\\" : "/");
string text2 = "Screenshot " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".png";
return SaveSourceScreenshot(recordingDirectory + text + text2);
}
public static bool SaveSourceScreenshot(string imageFilePath)
{
return SaveSourceScreenshot(sceneUuid, imageFilePath);
}
public static bool SaveSourceScreenshot(string sourceUuid, string imageFilePath)
{
Dictionary<string, object> parameters = new Dictionary<string, object>
{
["sourceUuid"] = sourceUuid,
["imageFormat"] = "png",
["imageFilePath"] = imageFilePath
};
string text = SendRequest("SaveSourceScreenshot", parameters);
return text != null;
}
private void SetMainStatus()
{
try
{
isReplayBufferActive = GetReplayBufferStatus().outputActive;
isRecordingActive = GetRecordStatus().outputActive;
isStreamActive = GetStreamStatus().outputActive;
sceneUuid = GetCurrentProgramScene().sceneUuid;
isWindows = GetVersion().platform == "windows";
recordingDirectory = GetRecordDirectory().recordDirectory;
}
catch (Exception ex)
{
MelonLogger.Error(ex.Message);
}
}
}
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 class ScreenshotSaved
{
public string savedScreenshotPath { get; set; }
}
public object eventData { get; set; }
public int eventIntent { get; set; }
public string eventType { get; set; }
}
public static class BuildInfo
{
public const string ModName = "OBS_Control_API";
public const string ModVersion = "1.1.0";
public const string Description = "Manages a websocket connection to OBS";
public const string Author = "Kalamart";
public const string Company = "";
}
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 class GetCurrentProgramScene
{
public string sceneName { get; set; }
public string sceneUuid { get; set; }
public string currentProgramSceneName { get; set; }
public string currentProgramSceneUuid { get; set; }
}
public RequestStatus requestStatus { get; set; }
public string responseData { get; set; }
}
}