Decompiled source of StreamlabsIntegration v1.0.0

Mods/StreamlabsIntegration.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.WebSockets;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSLZ.Utilities;
using Il2CppSystem;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using StreamlabsIntegration;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "StreamlabsIntegration", "1.0.0", "notnotnotswipez", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("StreamlabsIntegration")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StreamlabsIntegration")]
[assembly: AssemblyTitle("StreamlabsIntegration")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StreamlabsIntegration
{
	public class EmbeddedResourceHelper
	{
		public static byte[] GetResourceBytes(string filename)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (!text.Contains(filename))
				{
					continue;
				}
				using Stream stream = executingAssembly.GetManifestResourceStream(text);
				if (stream == null)
				{
					return null;
				}
				byte[] array = new byte[stream.Length];
				stream.Read(array, 0, array.Length);
				return array;
			}
			return null;
		}
	}
	public static class AssetBundleExtension
	{
		public static T LoadPersistentAsset<T>(this AssetBundle bundle, string name) where T : Object
		{
			Object val = bundle.LoadAsset(name);
			if (val != (Object)null)
			{
				val.hideFlags = (HideFlags)32;
				return ((Il2CppObjectBase)val).TryCast<T>();
			}
			return default(T);
		}
	}
	public class ClipboardEffectAssets
	{
		public static Material goldMaterial;

		public static void LoadAssets(AssetBundle bundle)
		{
			goldMaterial = bundle.LoadPersistentAsset<Material>("assets/gold.mat");
		}
	}
	public class ClipboardNotifier
	{
		public string donator;

		public string monetaryDisplay;

		public string message;

		public float value;

		private const string CLIPBOARD_BARCODE = "SLZ.BONELAB.Content.Spawnable.ClipboardLore";

		private const string AURA_GLOW = "SLZ.BONELAB.Content.VFX.AuraGlow";

		private const float clipBoardDistanceToHead = 1f;

		public ClipboardNotifier(string donator, string monetaryDisplay, string message, float value)
		{
			this.donator = donator;
			this.monetaryDisplay = monetaryDisplay;
			this.message = message;
			this.value = value;
		}

		public void SpawnClipboard()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			GameObjectCrate val = ((Il2CppObjectBase)AssetWarehouse.Instance._crateRegistry[new Barcode("SLZ.BONELAB.Content.Spawnable.ClipboardLore")]).TryCast<GameObjectCrate>();
			GameObjectCrate auraGlowCrate = ((Il2CppObjectBase)AssetWarehouse.Instance._crateRegistry[new Barcode("SLZ.BONELAB.Content.VFX.AuraGlow")]).TryCast<GameObjectCrate>();
			GameObject disabledParent = new GameObject("DISABLED_CONTAINER");
			disabledParent.SetActive(false);
			Action<GameObject> action = delegate(GameObject go)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_00db: Unknown result type (might be due to invalid IL or missing references)
				Transform head = ((Rig)PlayerRefs.Instance.PlayerPhysicsRig).m_head;
				Vector3 determinedPosition = head.position + head.forward * 1f;
				Vector3 forward = head.position - determinedPosition;
				GameObject spawnedClip = Object.Instantiate<GameObject>(go, determinedPosition, Quaternion.identity, disabledParent.transform);
				Object.DestroyImmediate((Object)(object)spawnedClip.GetComponent<LoreClipboard>());
				spawnedClip.transform.parent = null;
				Object.DestroyImmediate((Object)(object)disabledParent);
				Rigidbody component = spawnedClip.GetComponent<Rigidbody>();
				component.isKinematic = true;
				spawnedClip.transform.forward = forward;
				spawnedClip.transform.Rotate(new Vector3(90f, 0f, 0f), (Space)1);
				component.isKinematic = false;
				FixedJoint val2 = spawnedClip.AddComponent<FixedJoint>();
				((Joint)val2).breakForce = 900f;
				((Joint)val2).breakTorque = 900f;
				Action<GameObject> action2 = delegate(GameObject go)
				{
					//IL_000a: Unknown result type (might be due to invalid IL or missing references)
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0035: Expected O, but got Unknown
					GameObject spawnedVFX = Object.Instantiate<GameObject>(go, determinedPosition, Quaternion.identity);
					GenericOnJointBreak val3 = spawnedClip.AddComponent<GenericOnJointBreak>();
					val3.JointBreakEvent = new UnityEvent();
					val3.JointBreakEvent.AddListener(UnityAction.op_Implicit((Action)delegate
					{
						Object.Destroy((Object)(object)spawnedVFX);
					}));
				};
				((CrateT<GameObject>)(object)auraGlowCrate).LoadAsset(Action<GameObject>.op_Implicit(action2));
				TMP_Text component2 = ((Component)spawnedClip.transform.Find("Text/textTitle")).GetComponent<TMP_Text>();
				TMP_Text component3 = ((Component)spawnedClip.transform.Find("Text/textBody")).GetComponent<TMP_Text>();
				component2.text = donator + " (" + monetaryDisplay + ")";
				component3.text = message;
				OnClipboardAfterEffect(spawnedClip, component);
			};
			((CrateT<GameObject>)(object)val).LoadAsset(Action<GameObject>.op_Implicit(action));
		}

		public void OnClipboardAfterEffect(GameObject clipBoard, Rigidbody body)
		{
			if (!((double)value > 24.9))
			{
				return;
			}
			Transform val = clipBoard.transform.Find("Art");
			foreach (MeshRenderer componentsInChild in ((Component)val).GetComponentsInChildren<MeshRenderer>())
			{
				((Renderer)componentsInChild).materials = Il2CppReferenceArray<Material>.op_Implicit((Material[])(object)new Material[2]
				{
					ClipboardEffectAssets.goldMaterial,
					ClipboardEffectAssets.goldMaterial
				});
			}
			body.mass = 30f;
		}
	}
	public class Core : MelonMod
	{
		[HarmonyPatch(typeof(UIRig), "Awake")]
		public class UIRigAwakePatch
		{
			public static void Postfix()
			{
				canDisplayClipboards = true;
			}
		}

		[HarmonyPatch(typeof(LoadingScene), "Start")]
		public class LoadingSceneStartPatch
		{
			public static void Postfix()
			{
				canDisplayClipboards = false;
			}
		}

		private static string latestMessage = "";

		private static ClientWebSocket cSocket;

		public static Action<float, string, string> OnDonationEvent;

		private static Dictionary<string, float> toUSDConversion = new Dictionary<string, float>
		{
			{ "USD", 1f },
			{ "AUD", 0.63f },
			{ "BRL", 0.174f },
			{ "CAD", 0.7f },
			{ "CZK", 0.043f },
			{ "DKK", 0.14f },
			{ "EUR", 1.07f },
			{ "HKD", 0.13f },
			{ "ILS", 0.27f },
			{ "MYR", 0.22f },
			{ "MXN", 0.049f },
			{ "NOK", 0.095f },
			{ "NZD", 0.57f },
			{ "PHP", 0.017f },
			{ "PLN", 0.26f },
			{ "GBP", 1.29f },
			{ "RUB", 0.012f },
			{ "SGD", 0.75f },
			{ "SEK", 0.099f },
			{ "CHF", 1.13f },
			{ "THB", 0.029f },
			{ "TRY", 0.026f }
		};

		public static bool canDisplayClipboards = false;

		private Queue<ClipboardNotifier> queuedClipboards = new Queue<ClipboardNotifier>();

		private MelonPreferences_Entry<float> minimumClipboardDonation;

		public override void OnInitializeMelon()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)MelonUtils.CurrentPlatform == 3)
			{
				MelonLogger.Error("StreamlabsIntegration not supported on Quest!");
				return;
			}
			MelonPreferences_Category val = MelonPreferences.CreateCategory("StreamlabsIntegration");
			MelonPreferences_Entry<string> val2 = val.CreateEntry<string>("socketToken", "none", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			minimumClipboardDonation = val.CreateEntry<float>("minimumClipboardDonation", 4.8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			string value = val2.Value;
			val.SaveToFile(false);
			if (!(value == "none"))
			{
				ClipboardEffectAssets.LoadAssets(AssetBundle.LoadFromMemory(Il2CppStructArray<byte>.op_Implicit(EmbeddedResourceHelper.GetResourceBytes("streamlabs.assets"))));
				string uriString = "wss://sockets.streamlabs.com/socket.io/?token=" + value + "&EIO=3&transport=websocket";
				((MelonBase)this).LoggerInstance.Msg("CONNECTING TO STREAMLABS SOCKET...");
				cSocket = new ClientWebSocket();
				Task task = cSocket.ConnectAsync(new Uri(uriString), CancellationToken.None);
				while (!task.GetAwaiter().IsCompleted)
				{
				}
				if (cSocket.State == WebSocketState.Open)
				{
					((MelonBase)this).LoggerInstance.Msg("Connected to StreamLabs.");
					RunSocketThread();
				}
				else
				{
					((MelonBase)this).LoggerInstance.Msg("Failed to connect to StreamLabs.");
				}
			}
		}

		public override void OnUpdate()
		{
			if (canDisplayClipboards && queuedClipboards.Count > 0)
			{
				ClipboardNotifier clipboardNotifier = queuedClipboards.Dequeue();
				clipboardNotifier.SpawnClipboard();
			}
			if (latestMessage.Length <= 0)
			{
				return;
			}
			try
			{
				if (latestMessage.Contains("streamlabels.underlying"))
				{
					latestMessage = "";
					return;
				}
				dynamic val = JsonConvert.DeserializeObject<object>(latestMessage);
				dynamic val2 = val[1];
				string text = val2["type"];
				string text2 = "";
				float num = 0f;
				string text3 = "";
				string monetaryDisplay = "";
				string text4 = "";
				bool flag = true;
				switch (text)
				{
				case "superchat":
				{
					dynamic val3 = val2["message"][0];
					text2 = val3["amount"];
					num = float.Parse(text2) / 1000000f;
					text3 = val3["name"];
					monetaryDisplay = val3["displayString"];
					text4 = val3["comment"];
					string key = val3["currency"];
					if (toUSDConversion.ContainsKey(key))
					{
						num *= toUSDConversion[key];
					}
					else
					{
						flag = false;
					}
					break;
				}
				case "bits":
				{
					dynamic val3 = val2["message"][0];
					text2 = val3["amount"];
					num = float.Parse(text2) / 100f;
					text3 = val3["name"];
					monetaryDisplay = text2 + " Bits";
					text4 = val3["message"];
					break;
				}
				case "subscription":
				{
					dynamic val3 = val2["message"][0];
					string text5 = val2["for"];
					text2 = val3["months"];
					num = 5f;
					text3 = val3["name"];
					monetaryDisplay = text2 + " Month Sub!";
					if (text5 == "twitch_account")
					{
						text4 = val3["message"];
					}
					else if (text5 == "youtube_account")
					{
						text4 = "From YouTube!";
						monetaryDisplay = text2 + " Month Member!";
					}
					break;
				}
				default:
					flag = false;
					break;
				}
				if (flag && (double)num > 0.5 && text != "subscription")
				{
					OnDonationEvent?.Invoke(num, text3, text4);
				}
				if (flag && num > minimumClipboardDonation.Value)
				{
					queuedClipboards.Enqueue(new ClipboardNotifier(text3, monetaryDisplay, text4, num));
				}
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Msg(latestMessage);
				MelonLogger.Error("Error when parsing streamlabs event: " + ex);
			}
			latestMessage = "";
		}

		public void RunSocketThread()
		{
			Thread thread = new Thread((ThreadStart)delegate
			{
				byte[] array = new byte[4096];
				while (cSocket.State == WebSocketState.Open)
				{
					Task<WebSocketReceiveResult> task = cSocket.ReceiveAsync(new ArraySegment<byte>(array), CancellationToken.None);
					while (!task.GetAwaiter().IsCompleted)
					{
					}
					string @string = Encoding.UTF8.GetString(array, 0, task.Result.Count);
					if (@string.StartsWith("42"))
					{
						latestMessage = @string.Substring(2);
					}
				}
			});
			Thread thread2 = new Thread((ThreadStart)delegate
			{
				while (cSocket.State == WebSocketState.Open)
				{
					cSocket.SendAsync(Encoding.UTF8.GetBytes("2"), WebSocketMessageType.Text, endOfMessage: true, CancellationToken.None);
					Thread.Sleep(3000);
				}
			});
			thread.Start();
			thread2.Start();
		}
	}
}