Decompiled source of RumbleReplay v1.1.3

Mods/RumbleReplayMod.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppRUMBLE.Players;
using Il2CppRUMBLE.Players.Scaling;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Text;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using RumbleModdingAPI;
using RumbleReplay;
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(RumbleReplayModClass), "RumbleReplay", "1.1.3", "Blank", null)]
[assembly: MelonGame("Buckethead Entertainment", "RUMBLE")]
[assembly: MelonAdditionalDependencies(new string[] { "RumbleModdingAPI" })]
[assembly: MelonColor(255, 255, 170, 238)]
[assembly: AssemblyTitle("RumbleReplay")]
[assembly: AssemblyDescription("Generates Replay Files for use in blender or other supported programs")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RumbleReplay")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7eebd5a9-aa24-4565-9bca-aad698946213")]
[assembly: AssemblyFileVersion("1.1.3")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.1.3.0")]
[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 RumbleReplay
{
	public static class BuildInfo
	{
		public const string Name = "RumbleReplay";

		public const string Author = "Blank";

		public const string Version = "1.1.3";
	}
	public sealed class RumbleReplayModClass : MelonMod
	{
		public sealed class ReplayPlayerData
		{
			public string Name = "Unknown";

			public int Battlepoints;

			public string PlayfabID = "Unknown";

			public string Cosmetics = PlayerVisualData.DefaultFemale.ToPlayfabDataString();
		}

		public sealed class ReplayHeader
		{
			public readonly string Version = "1.1.3";

			public ReplayPlayerData LocalPlayer = new ReplayPlayerData();

			public ReplayPlayerData RemotePlayer = new ReplayPlayerData();

			public string Scene = "Unknown";

			public readonly string Date = DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss");
		}

		private readonly GameObject[] _poolObjects = (GameObject[])(object)new GameObject[8];

		private readonly int[][] _cullers = new int[8][];

		private readonly List<byte> _writebuffer = new List<byte>();

		private MelonPreferences_Category _rumbleReplayPreferences;

		private MelonPreferences_Entry<int> _playerUpdateInterval;

		private MelonPreferences_Entry<int> _basicStructureUpdateInterval;

		private MelonPreferences_Entry<bool> _enabled;

		public bool Recording;

		public short FrameCounter;

		private string _currentScene;

		private FileStream _replayFile;

		private BinaryWriter _replayWriter;

		private static List<byte> SerializeTransform(Transform transform, bool includeRotation = true, bool includePosition = true, bool includeScale = false)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0089: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			List<byte> list = new List<byte>();
			if (includePosition)
			{
				list.AddRange(BitConverter.GetBytes(transform.position.x));
				list.AddRange(BitConverter.GetBytes(transform.position.y));
				list.AddRange(BitConverter.GetBytes(transform.position.z));
			}
			if (includeRotation)
			{
				list.AddRange(BitConverter.GetBytes(transform.rotation.w));
				list.AddRange(BitConverter.GetBytes(transform.rotation.y));
				list.AddRange(BitConverter.GetBytes(transform.rotation.x));
				list.AddRange(BitConverter.GetBytes(transform.rotation.z));
			}
			if (includeScale)
			{
				list.AddRange(BitConverter.GetBytes(transform.localScale.x));
				list.AddRange(BitConverter.GetBytes(transform.localScale.y));
				list.AddRange(BitConverter.GetBytes(transform.localScale.z));
			}
			return list;
		}

		public void NewReplay(string scene, ReplayPlayerData localPlayer, ReplayPlayerData remotePlayer)
		{
			if (_enabled.Value)
			{
				ReplayHeader replayHeader = new ReplayHeader
				{
					LocalPlayer = localPlayer,
					RemotePlayer = remotePlayer,
					Scene = scene
				};
				string text = JsonConvert.SerializeObject((object)replayHeader);
				if (_replayFile != null)
				{
					StopReplay();
				}
				((MelonBase)this).LoggerInstance.Msg("Recording Started");
				if (!Directory.Exists("UserData/Replays"))
				{
					Directory.CreateDirectory("UserData/Replays");
				}
				_replayFile = File.Create("UserData/Replays/" + localPlayer.Name + "-Vs-" + remotePlayer.Name + " On " + scene + "-" + Path.GetRandomFileName() + ".rr");
				_replayWriter = new BinaryWriter(_replayFile);
				byte[] buffer = new byte[2] { 82, 82 };
				_replayWriter.Write(buffer);
				_replayWriter.Write((short)text.Length);
				_replayWriter.Write(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)Encoding.UTF8.GetBytes(text)));
				Recording = true;
			}
		}

		public void StopReplay()
		{
			if (_replayFile == null)
			{
				((MelonBase)this).LoggerInstance.Warning("StopReplay with null replay file");
				return;
			}
			_replayWriter.Write(_writebuffer.ToArray());
			_writebuffer.Clear();
			Recording = false;
			FrameCounter = 0;
			((MelonBase)this).LoggerInstance.Msg("Recording Stopped");
			_replayFile.Close();
			_replayFile = null;
			_replayWriter = null;
		}

		public override void OnLateInitializeMelon()
		{
			Calls.onMapInitialized += MapReady;
		}

		public override void OnInitializeMelon()
		{
			_rumbleReplayPreferences = MelonPreferences.CreateCategory("RumbleReplaySettings");
			_rumbleReplayPreferences.SetFilePath("UserData/RumbleReplay.cfg");
			_playerUpdateInterval = _rumbleReplayPreferences.CreateEntry<int>("PlayerUpdate_Interval", 4, (string)null, "The interval we create updates for the players Hands and Head", false, false, (ValueValidator)null, (string)null);
			_basicStructureUpdateInterval = _rumbleReplayPreferences.CreateEntry<int>("BasicStructureUpdate_Interval", 1, (string)null, "the interval structure positions and rotations are updated (Leave at 1 for 1 update every physics frame, 2 for one update every 2 and so on)", false, false, (ValueValidator)null, (string)null);
			_enabled = _rumbleReplayPreferences.CreateEntry<bool>("RecordingEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_rumbleReplayPreferences.SaveToFile(true);
			((MelonBase)this).LoggerInstance.Msg($"PlayerUpdate_Interval={_playerUpdateInterval.Value}");
			((MelonBase)this).LoggerInstance.Msg($"BasicStructureUpdate_Interval={_basicStructureUpdateInterval.Value}");
			((MelonBase)this).LoggerInstance.Msg($"Enabled {_enabled.Value}");
		}

		public override void OnSceneWasLoaded(int _, string sceneName)
		{
			if (Recording)
			{
				StopReplay();
			}
			_currentScene = sceneName;
		}

		private void MapReady()
		{
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			if (!(_currentScene != "Loader") || !(_currentScene != "Park") || !(_currentScene != "Gym"))
			{
				return;
			}
			((MelonBase)this).LoggerInstance.Msg("Loaded scene: " + _currentScene);
			_poolObjects[0] = Structures.GetPoolBall();
			_poolObjects[1] = Structures.GetPoolBoulderBall();
			_poolObjects[2] = Structures.GetPoolCube();
			_poolObjects[3] = Structures.GetPoolDisc();
			_poolObjects[4] = Structures.GetPoolLargeRock();
			_poolObjects[5] = Structures.GetPoolPillar();
			_poolObjects[6] = Structures.GetPoolWall();
			_poolObjects[7] = Structures.GetPoolSmallRock();
			for (ushort num = 0; num < _poolObjects.Length; num++)
			{
				GameObject val = _poolObjects[num];
				_cullers[num] = new int[val.transform.GetChildCount()];
				((MelonBase)this).LoggerInstance.Msg((object)_cullers[num].Length);
				((MelonBase)this).LoggerInstance.Msg(((Object)val.transform.GetChild(0)).name);
				for (ushort num2 = 0; num2 < val.transform.GetChildCount(); num2++)
				{
					GameObject gameObject = ((Component)val.transform.GetChild((int)num2)).gameObject;
					int[] obj = _cullers[num];
					ushort num3 = num2;
					Vector3 position = gameObject.transform.position;
					obj[num3] = ((object)(Vector3)(ref position)).GetHashCode();
				}
			}
			ReplayPlayerData replayPlayerData = new ReplayPlayerData();
			Player localPlayer = Managers.GetPlayerManager().LocalPlayer;
			replayPlayerData.Name = Regex.Replace(((localPlayer != null) ? localPlayer.Data.GeneralData.PublicUsername : null) ?? "Unknown", "<.*?>|[^a-zA-Z0-9_ ]", "");
			Player localPlayer2 = Managers.GetPlayerManager().LocalPlayer;
			replayPlayerData.Battlepoints = ((localPlayer2 != null) ? localPlayer2.Data.GeneralData.BattlePoints : 0);
			Player localPlayer3 = Managers.GetPlayerManager().LocalPlayer;
			replayPlayerData.PlayfabID = ((localPlayer3 != null) ? localPlayer3.Data.GeneralData.PlayFabMasterId : null) ?? "Unknown";
			Player localPlayer4 = Managers.GetPlayerManager().LocalPlayer;
			replayPlayerData.Cosmetics = ((localPlayer4 != null) ? localPlayer4.Data.VisualData.ToPlayfabDataString() : null) ?? PlayerVisualData.DefaultFemale.ToPlayfabDataString();
			ReplayPlayerData replayPlayerData2 = replayPlayerData;
			ReplayPlayerData replayPlayerData3 = new ReplayPlayerData();
			Player? obj2 = Players.GetEnemyPlayers().FirstOrDefault();
			replayPlayerData3.Name = Regex.Replace(((obj2 != null) ? obj2.Data.GeneralData.PublicUsername : null) ?? "Unknown", "<.*?>|[^a-zA-Z0-9_ ]", "");
			Player? obj3 = Players.GetEnemyPlayers().FirstOrDefault();
			replayPlayerData3.Battlepoints = ((obj3 != null) ? obj3.Data.GeneralData.BattlePoints : 0);
			Player? obj4 = Players.GetEnemyPlayers().FirstOrDefault();
			replayPlayerData3.PlayfabID = ((obj4 != null) ? obj4.Data.GeneralData.PlayFabMasterId : null) ?? "Unknown";
			Player? obj5 = Players.GetEnemyPlayers().FirstOrDefault();
			replayPlayerData3.Cosmetics = ((obj5 != null) ? obj5.Data.VisualData.ToPlayfabDataString() : null) ?? PlayerVisualData.DefaultFemale.ToPlayfabDataString();
			ReplayPlayerData replayPlayerData4 = replayPlayerData3;
			((MelonBase)this).LoggerInstance.Msg(replayPlayerData2.Name);
			((MelonBase)this).LoggerInstance.Msg(replayPlayerData4.Name);
			NewReplay(_currentScene, replayPlayerData2, replayPlayerData4);
		}

		public override void OnApplicationQuit()
		{
			if (Recording)
			{
				StopReplay();
			}
		}

		private static List<byte> _createPlayerUpdate()
		{
			int num = 0;
			List<byte> list = new List<byte>();
			Enumerator<Player> enumerator = Managers.GetPlayerManager().AllPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Player current = enumerator.Current;
				RigDefinition val = ((current != null) ? ((Component)current.Controller).GetComponentInChildren<RigDefinition>() : null);
				Transform transform = val.headDefinition.Transform;
				Transform transform2 = val.spineDefinition.Transform;
				Transform transform3 = val.leftHandDefinition.Transform;
				Transform transform4 = val.rightHandDefinition.Transform;
				Transform transform5 = val.leftFootDefinition.Transform;
				Transform transform6 = val.rightFootDefinition.Transform;
				list.Add((byte)num);
				list.AddRange(SerializeTransform(transform));
				list.AddRange(SerializeTransform(transform2));
				list.AddRange(SerializeTransform(transform3));
				list.AddRange(SerializeTransform(transform4));
				list.AddRange(SerializeTransform(transform5));
				list.AddRange(SerializeTransform(transform6));
				num++;
			}
			return list;
		}

		private List<byte> _createBasicStructureUpdate()
		{
			//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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			List<byte> list = new List<byte>();
			for (ushort num = 0; num < _poolObjects.Length; num++)
			{
				GameObject val = _poolObjects[num];
				for (ushort num2 = 0; num2 < _cullers[num].Length; num2++)
				{
					GameObject gameObject = ((Component)val.transform.GetChild((int)num2)).gameObject;
					int num3 = _cullers[num][num2];
					Vector3 position = gameObject.transform.position;
					if (num3 != ((object)(Vector3)(ref position)).GetHashCode())
					{
						int[] obj = _cullers[num];
						ushort num4 = num2;
						position = gameObject.transform.position;
						obj[num4] = ((object)(Vector3)(ref position)).GetHashCode();
						list.Add((byte)num);
						list.Add((byte)num2);
						Transform transform = gameObject.transform;
						if (!gameObject.active)
						{
							transform.position = new Vector3(0f, -300f, 0f);
						}
						list.AddRange(SerializeTransform(transform));
					}
				}
			}
			return list;
		}

		public override void OnFixedUpdate()
		{
			if (Recording)
			{
				List<byte> list = new List<byte>();
				if (FrameCounter % _playerUpdateInterval.Value == 0)
				{
					list.AddRange(_createPlayerUpdate());
				}
				List<byte> list2 = new List<byte>();
				if (FrameCounter % _basicStructureUpdateInterval.Value == 0)
				{
					list2.AddRange(_createBasicStructureUpdate());
				}
				if (list2.Count != 0)
				{
					_writebuffer.AddRange(BitConverter.GetBytes((short)list2.Count));
					_writebuffer.AddRange(BitConverter.GetBytes(FrameCounter));
					_writebuffer.Add(0);
					_writebuffer.AddRange(list2);
				}
				if (list.Count != 0)
				{
					_writebuffer.AddRange(BitConverter.GetBytes((short)list.Count));
					_writebuffer.AddRange(BitConverter.GetBytes(FrameCounter));
					_writebuffer.Add(2);
					_writebuffer.AddRange(list);
				}
				FrameCounter++;
				if (_writebuffer.Count >= 1000)
				{
					_replayWriter.Write(_writebuffer.ToArray());
					_writebuffer.Clear();
				}
			}
		}
	}
}