Decompiled source of GTFOReplay v0.0.4
ReplayRecorder.dll
Decompiled 4 months ago
The result has been truncated due to the large size, download it to view full contents!
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.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using AIGraph; using API; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using ChainedPuzzles; using Enemies; using GameData; using Gear; using Globals; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Localization; using Microsoft.CodeAnalysis; using Player; using ReplayRecorder; using ReplayRecorder.ChainedPuzzle; using ReplayRecorder.Dimensions; using ReplayRecorder.Enemies; using ReplayRecorder.Enemies.Patches; using ReplayRecorder.Map; using ReplayRecorder.Map.Patches; using ReplayRecorder.Mines; using ReplayRecorder.Player; using SNetwork; using UnityEngine; using UnityEngine.AI; using UnityEngine.Analytics; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ReplayRecorder")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+32e89ab0cced64a6c2434703c1ffa46f5f11fd20")] [assembly: AssemblyProduct("ReplayRecorder")] [assembly: AssemblyTitle("ReplayRecorder")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] 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 ReplayRecorder { public static class Module { public const string GUID = "randomuserhi.ReplayRecorder"; public const string Name = "ReplayRecorder"; public const string Version = "0.0.1"; } internal static class ConfigManager { private static ConfigEntry<bool> debug; private static ConfigEntry<string> replayFolder; private static ConfigEntry<string> replayFilename; private static ConfigEntry<int> pelletLingerTime; public static bool Debug { get { return debug.Value; } set { debug.Value = value; } } public static string ReplayFolder { get { return replayFolder.Value; } set { replayFolder.Value = value; } } public static string ReplayFileName { get { return replayFilename.Value; } set { replayFilename.Value = value; } } public static int PelletLingerTime { get { return pelletLingerTime.Value; } set { pelletLingerTime.Value = value; } } static ConfigManager() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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) ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "ReplayRecorder.cfg"), true); debug = val.Bind<bool>("Debug", "enable", false, "Enables debug messages when true."); replayFolder = val.Bind<string>("Settings", "replayFolder", "./", "Location of which replays will be stored. If invalid, defaults to the games location."); replayFilename = val.Bind<string>("Settings", "replayFilename", "{0} {1:yyyy-MM-dd HH-mm}", "Filename format of stored replays. Follows C# string format syntax with (0: Rundown + Level, 1: Date). If filename is invalid (contains invalid characters etc...) default name of 'replay' is used."); pelletLingerTime = val.Bind<int>("ReplayRecorder", "lingerTime", 500, "Time projectiles linger post removal."); } } [HarmonyPatch] internal class GameEventManager { [HarmonyPatch(typeof(RundownManager), "EndGameSession")] [HarmonyPrefix] private static void EndGameSession() { APILogger.Debug("Level ended!"); SnapshotManager.Dispose(); ReplayRecorder.Map.Map.Reset(); Mine.Reset(); Enemy.Reset(); PlayerSentry.Reset(); ReplayRecorder.ChainedPuzzle.ChainedPuzzle.Reset(); } } internal struct GTFOSpecification { private static GTFOSpecification defaultSpec = new GTFOSpecification { items = new Dictionary<uint, byte> { { uint.MaxValue, 0 } }, enemies = new Dictionary<uint, byte> { { uint.MaxValue, 0 }, { 20u, 1 }, { 40u, 2 }, { 41u, 3 }, { 21u, 4 }, { 35u, 5 }, { 38u, 6 }, { 48u, 6 }, { 13u, 7 }, { 32u, 7 }, { 31u, 7 }, { 24u, 7 }, { 49u, 7 }, { 16u, 8 }, { 28u, 8 }, { 50u, 8 }, { 26u, 9 }, { 51u, 9 }, { 11u, 9 }, { 18u, 10 }, { 33u, 11 }, { 30u, 12 }, { 39u, 13 }, { 29u, 14 }, { 36u, 15 }, { 37u, 16 }, { 46u, 17 }, { 47u, 18 }, { 42u, 19 }, { 45u, 20 }, { 43u, 21 }, { 44u, 22 }, { 53u, 23 }, { 52u, 24 }, { 54u, 25 } } }; private static GTFOSpecification spec = defaultSpec; public Dictionary<uint, byte> enemies { get; set; } public Dictionary<uint, byte> items { get; set; } public static GTFOSpecification LoadSpecification(string filepath) { spec = JsonSerializer.Deserialize<GTFOSpecification>(File.ReadAllText(filepath)); return spec; } public static byte GetEnemyType(uint type) { if (spec.enemies.ContainsKey(type)) { return spec.enemies[type]; } APILogger.Error($"Unknown enemy type, {type}, found. Please create a custom specification and add it."); return 0; } public static byte GetItem(uint item) { if (spec.items.ContainsKey(item)) { return spec.items[item]; } APILogger.Error($"Unknown item type, {item}, found. Please create a custom specification and add it."); return 0; } } [HarmonyPatch] internal class Network { private static byte msgtype = 172; private static uint magickey = 10992881u; private static ushort repKey = 65531; public static void SendReplay(byte[] replay) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if (!SNet.IsMaster) { return; } SNet_ChannelType val = (SNet_ChannelType)0; SNet_SendGroup val2 = default(SNet_SendGroup); SNet_SendQuality val3 = default(SNet_SendQuality); int num = default(int); SNet.GetSendSettings(ref val, ref val2, ref val3, ref num); List<SNet_Player> val4 = new List<SNet_Player>(PlayerManager.PlayerAgentsInLevel.Count); for (int i = 0; i < PlayerManager.PlayerAgentsInLevel.Count; i++) { if (!((Agent)PlayerManager.PlayerAgentsInLevel[i]).IsLocallyOwned && !PlayerManager.PlayerAgentsInLevel[i].Owner.IsBot) { val4.Add(PlayerManager.PlayerAgentsInLevel[i].Owner); APILogger.Debug("[Networking] Sending report to " + PlayerManager.PlayerAgentsInLevel[i].PlayerName); } } APILogger.Debug($"[Networking] Replay is {replay.Length} bytes large."); byte[] array = new byte[11]; Array.Copy(BitConverter.GetBytes(repKey), 0, array, 0, 2); Array.Copy(BitConverter.GetBytes(magickey), 0, array, 2, 4); array[6] = msgtype; Array.Copy(BitConverter.GetBytes(replay.Length), 0, array, 7, 4); byte[] array2 = new byte[array.Length + replay.Length]; Array.Copy(array, 0, array2, 0, array.Length); Array.Copy(replay, 0, array2, array.Length, replay.Length); SNet.Core.SendBytes(Il2CppStructArray<byte>.op_Implicit(array2), val3, num, val4); } [HarmonyPatch(typeof(SNet_Replication), "RecieveBytes")] [HarmonyWrapSafe] [HarmonyPrefix] private static bool RecieveBytes_Prefix(Il2CppStructArray<byte> bytes, uint size, ulong messagerID) { if (size < 12) { return true; } ushort num = BitConverter.ToUInt16(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)bytes), 0); if (repKey == num) { if (BitConverter.ToUInt32(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)bytes), 2) != magickey) { APILogger.Debug("[Networking] Magic key is incorrect."); return true; } byte b = ((Il2CppArrayBase<byte>)(object)bytes)[6]; if (b != msgtype) { if (msgtype == 173) { return true; } APILogger.Debug($"[Networking] msg type is incorrect. {b} {msgtype}"); return true; } int num2 = BitConverter.ToInt32(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)bytes), 7); byte[] array = new byte[num2]; Array.Copy(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)bytes), 11, array, 0, num2); if (SnapshotManager.active) { APILogger.Debug("Snapshot manager was still running, assuming end of run and closing..."); SnapshotManager.Dispose(); } APILogger.Debug($"[Networking] Report received: {num2} bytes"); File.WriteAllBytes(SnapshotManager.fullpath, array); return false; } return true; } } [BepInPlugin("randomuserhi.ReplayRecorder", "ReplayRecorder", "0.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { private static Harmony? harmony; public override void Load() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown APILogger.Log("Plugin is loaded!"); harmony = new Harmony("randomuserhi.ReplayRecorder"); harmony.PatchAll(); if (((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("randomuserhi.BetterChat", out var _)) { APILogger.Log("BetterChat is installed, adding commands."); } APILogger.Log("Debug is " + (ConfigManager.Debug ? "Enabled" : "Disabled")); ClassInjector.RegisterTypeInIl2Cpp<SnapshotManager>(); SnapshotManager.Setup(); } } internal static class Raudy { public static long Now => ((DateTimeOffset)DateTime.Now).ToUnixTimeMilliseconds(); } internal static class BitHelper { public const int SizeOfHalf = 2; public const int SizeOfVector3 = 12; public const int SizeOfQuaternion = 13; public const int SizeOfHalfVector3 = 6; public const int SizeOfHalfQuaternion = 7; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint RotateLeft(uint value, int offset) { return (value << offset) | (value >> 32 - offset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint RotateRight(uint value, int offset) { return (value >> offset) | (value << 32 - offset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReverseEndianness(long value) { return (long)ReverseEndianness((ulong)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReverseEndianness(int value) { return (int)ReverseEndianness((uint)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ReverseEndianness(short value) { return (short)ReverseEndianness((ushort)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort ReverseEndianness(ushort value) { return (ushort)((value >> 8) + (value << 8)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint ReverseEndianness(uint value) { return RotateRight(value & 0xFF00FFu, 8) + RotateLeft(value & 0xFF00FF00u, 8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong ReverseEndianness(ulong value) { return ((ulong)ReverseEndianness((uint)value) << 32) + ReverseEndianness((uint)(value >> 32)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void _WriteBytes(byte* source, int size, byte[] destination, ref int index) { int num = 0; while (num < size) { destination[index++] = source[num++]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteBytes(byte value, byte[] destination, ref int index) { destination[index++] = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(ulong value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 8, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(uint value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 4, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(ushort value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 2, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(long value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 8, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(int value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 4, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(short value, byte[] destination, ref int index) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } _WriteBytes((byte*)(&value), 2, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void WriteBytes(float value, byte[] destination, ref int index) { int value2 = *(int*)(&value); if (!BitConverter.IsLittleEndian) { value2 = ReverseEndianness(value2); } _WriteBytes((byte*)(&value2), 4, destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteBytes(string value, byte[] destination, ref int index) { byte[] bytes = Encoding.UTF8.GetBytes(value); WriteBytes((ushort)bytes.Length, destination, ref index); Array.Copy(bytes, 0, destination, index, bytes.Length); index += bytes.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteBytes(byte[] buffer, byte[] destination, ref int index) { WriteBytes((ushort)buffer.Length, destination, ref index); Array.Copy(buffer, 0, destination, index, buffer.Length); index += buffer.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteHalf(float value, byte[] destination, ref int index) { WriteBytes(FloatToHalf(value), destination, ref index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static uint AsUInt(float x) { return *(uint*)(&x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static float AsFloat(uint x) { return *(float*)(&x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float HalfToFloat(ushort x) { int num = (x & 0x7C00) >> 10; int num2 = (x & 0x3FF) << 13; int num3 = (int)(AsUInt(num2) >> 23); return AsFloat((uint)(((x & 0x8000) << 16) | (Convert.ToInt32(num != 0) * ((num + 112 << 23) | num2)) | ((Convert.ToInt32(num == 0) & Convert.ToInt32(num2 != 0)) * ((num3 - 37 << 23) | ((num2 << 150 - num3) & 0x7FE000))))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort FloatToHalf(float x) { uint num = AsUInt(x) + 4096; uint num2 = (num & 0x7F800000) >> 23; uint num3 = num & 0x7FFFFFu; return (ushort)(((num & 0x80000000u) >> 16) | (Convert.ToInt32(num2 > 112) * (((num2 - 112 << 10) & 0x7C00) | (num3 >> 13))) | ((Convert.ToInt32(num2 < 113) & Convert.ToInt32(num2 > 101)) * ((8384512 + num3 >> (int)(125 - num2)) + 1 >> 1)) | (Convert.ToUInt32(num2 > 143) * 32767)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ReadByte(byte[] source, ref int index) { return source[index++]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static ulong ReadULong(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 8; ulong num2 = *(ulong*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static long ReadLong(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 8; long num2 = *(long*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint ReadUInt(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 4; uint num2 = *(uint*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static int ReadInt(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 4; int num2 = *(int*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static ushort ReadUShort(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 2; ushort num2 = *(ushort*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static short ReadShort(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* num = ptr + index; index += 2; short num2 = *(short*)num; if (!BitConverter.IsLittleEndian) { num2 = ReverseEndianness(num2); } return num2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ReadHalf(byte[] source, ref int index) { return HalfToFloat(ReadUShort(source, ref index)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static float ReadFloat(byte[] source, ref int index) { fixed (byte* ptr = source) { byte* ptr2 = ptr + index; index += 4; if (!BitConverter.IsLittleEndian) { int num = ReverseEndianness(*(int*)ptr2); return *(float*)(&num); } return *(float*)ptr2; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ReadString(byte[] source, ref int index) { int num = ReadUShort(source, ref index); string @string = Encoding.UTF8.GetString(source, index, num); index += num; return @string; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteBytes(Vector3 value, byte[] destination, ref int index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) WriteBytes(value.x, destination, ref index); WriteBytes(value.y, destination, ref index); WriteBytes(value.z, destination, ref index); } public static void WriteBytes(Quaternion value, byte[] destination, ref int index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) float num = value.x; byte b = 0; if (value.y > num) { num = value.y; b = 1; } if (value.z > num) { num = value.z; b = 2; } if (value.w > num) { num = value.w; b = 3; } WriteBytes(b, destination, ref index); switch (b) { case 0: if (value.x >= 0f) { WriteBytes(value.y, destination, ref index); WriteBytes(value.z, destination, ref index); WriteBytes(value.w, destination, ref index); } else { WriteBytes(0f - value.y, destination, ref index); WriteBytes(0f - value.z, destination, ref index); WriteBytes(0f - value.w, destination, ref index); } break; case 1: if (value.y >= 0f) { WriteBytes(value.x, destination, ref index); WriteBytes(value.z, destination, ref index); WriteBytes(value.w, destination, ref index); } else { WriteBytes(0f - value.x, destination, ref index); WriteBytes(0f - value.z, destination, ref index); WriteBytes(0f - value.w, destination, ref index); } break; case 2: if (value.z >= 0f) { WriteBytes(value.x, destination, ref index); WriteBytes(value.y, destination, ref index); WriteBytes(value.w, destination, ref index); } else { WriteBytes(0f - value.x, destination, ref index); WriteBytes(0f - value.y, destination, ref index); WriteBytes(0f - value.w, destination, ref index); } break; case 3: if (value.w >= 0f) { WriteBytes(value.x, destination, ref index); WriteBytes(value.y, destination, ref index); WriteBytes(value.z, destination, ref index); } else { WriteBytes(0f - value.x, destination, ref index); WriteBytes(0f - value.y, destination, ref index); WriteBytes(0f - value.z, destination, ref index); } break; } } public static Vector3 ReadVector3(byte[] source, ref int index) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(ReadFloat(source, ref index), ReadFloat(source, ref index), ReadFloat(source, ref index)); } public static Quaternion ReadQuaternion(byte[] source, ref int index) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) byte b = ReadByte(source, ref index); float num = 0f; float num2 = 0f; float num3 = 0f; float num4 = 0f; switch (b) { case 0: num2 = ReadFloat(source, ref index); num3 = ReadFloat(source, ref index); num4 = ReadFloat(source, ref index); num = Mathf.Sqrt(1f - num2 * num2 - num3 * num3 - num4 * num4); break; case 1: num = ReadFloat(source, ref index); num3 = ReadFloat(source, ref index); num4 = ReadFloat(source, ref index); num2 = Mathf.Sqrt(1f - num * num - num3 * num3 - num4 * num4); break; case 2: num = ReadFloat(source, ref index); num2 = ReadFloat(source, ref index); num4 = ReadFloat(source, ref index); num3 = Mathf.Sqrt(1f - num * num - num2 * num2 - num4 * num4); break; case 3: num = ReadFloat(source, ref index); num2 = ReadFloat(source, ref index); num3 = ReadFloat(source, ref index); num4 = Mathf.Sqrt(1f - num * num - num2 * num2 - num3 * num3); break; } return new Quaternion(num, num2, num3, num4); } public static void WriteHalf(Vector3 value, byte[] destination, ref int index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) WriteHalf(value.x, destination, ref index); WriteHalf(value.y, destination, ref index); WriteHalf(value.z, destination, ref index); } public static void WriteHalf(Quaternion value, byte[] destination, ref int index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) float num = value.x; byte b = 0; if (value.y > num) { num = value.y; b = 1; } if (value.z > num) { num = value.z; b = 2; } if (value.w > num) { num = value.w; b = 3; } WriteBytes(b, destination, ref index); switch (b) { case 0: if (value.x >= 0f) { WriteHalf(value.y, destination, ref index); WriteHalf(value.z, destination, ref index); WriteHalf(value.w, destination, ref index); } else { WriteHalf(0f - value.y, destination, ref index); WriteHalf(0f - value.z, destination, ref index); WriteHalf(0f - value.w, destination, ref index); } break; case 1: if (value.y >= 0f) { WriteHalf(value.x, destination, ref index); WriteHalf(value.z, destination, ref index); WriteHalf(value.w, destination, ref index); } else { WriteHalf(0f - value.x, destination, ref index); WriteHalf(0f - value.z, destination, ref index); WriteHalf(0f - value.w, destination, ref index); } break; case 2: if (value.z >= 0f) { WriteHalf(value.x, destination, ref index); WriteHalf(value.y, destination, ref index); WriteHalf(value.w, destination, ref index); } else { WriteHalf(0f - value.x, destination, ref index); WriteHalf(0f - value.y, destination, ref index); WriteHalf(0f - value.w, destination, ref index); } break; case 3: if (value.w >= 0f) { WriteHalf(value.x, destination, ref index); WriteHalf(value.y, destination, ref index); WriteHalf(value.z, destination, ref index); } else { WriteHalf(0f - value.x, destination, ref index); WriteHalf(0f - value.y, destination, ref index); WriteHalf(0f - value.z, destination, ref index); } break; } } public static Vector3 ReadHalfVector3(byte[] source, ref int index) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(ReadHalf(source, ref index), ReadHalf(source, ref index), ReadHalf(source, ref index)); } public static Quaternion ReadHalfQuaternion(byte[] source, ref int index) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) byte b = ReadByte(source, ref index); float num = 0f; float num2 = 0f; float num3 = 0f; float num4 = 0f; switch (b) { case 0: num2 = ReadHalf(source, ref index); num3 = ReadHalf(source, ref index); num4 = ReadHalf(source, ref index); num = Mathf.Sqrt(1f - num2 * num2 - num3 * num3 - num4 * num4); break; case 1: num = ReadHalf(source, ref index); num3 = ReadHalf(source, ref index); num4 = ReadHalf(source, ref index); num2 = Mathf.Sqrt(1f - num * num - num3 * num3 - num4 * num4); break; case 2: num = ReadHalf(source, ref index); num2 = ReadHalf(source, ref index); num4 = ReadHalf(source, ref index); num3 = Mathf.Sqrt(1f - num * num - num2 * num2 - num4 * num4); break; case 3: num = ReadHalf(source, ref index); num2 = ReadHalf(source, ref index); num3 = ReadHalf(source, ref index); num4 = Mathf.Sqrt(1f - num * num - num2 * num2 - num3 * num3); break; } return new Quaternion(num, num2, num3, num4); } } internal interface ISerializable { void Serialize(FileStream fs); } internal struct rInstance : ISerializable { private int instance; public const int SizeOf = 4; private static byte[] buffer = new byte[4]; public rInstance(int instance) { this.instance = instance; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(instance, buffer, ref index); fs.Write(buffer); } } internal class GameplayEvent { public enum Type { AddPlayer, RemovePlayer, SpawnEnemy, DespawnEnemy, EnemyDead, EnemyBehaviourChangeState, EnemyLocomotionChangeState, EnemyBulletDamage, EnemyMeleeDamage, EnemyMineDamage, PlayerDead, PlayerRevive, PlayerTongueDamage, PlayerMeleeDamage, PlayerPelletDamage, PlayerBulletDamage, PlayerMineDamage, PlayerFallDamage, PlayerPelletDodge, PlayerTongueDodge, PlayerWield, DoorChangeState, DoorDamage, SpawnMine, DespawnMine, ExplodeMine, MineTripLine, SpawnSentry, DespawnSentry, SpawnPellet, DespawnPellet, SpawnTongue, DespawnTongue, SetTongue, SpawnGlue, DespawnGlue, EnemyAlerted, EnemyScreamed, EnemyTargetSet, BulletShot, SpawnScanCircle, DespawnScanCircle, SpawnHolopath, DespawnHolopath, Checkpoint, SpawnTendril, DespawnTendril } public long timestamp; public Type type; public ISerializable? detail; public GameplayEvent(long timestamp, Type type, ISerializable? detail) { this.timestamp = timestamp; this.type = type; this.detail = detail; } } internal abstract class DynamicProperty : ISerializable { public enum Type { Tongue, Scan, Holopath } public int instance; public Type type; public bool remove; private static byte[] buffer = new byte[4]; public bool Serializable { get { if (!remove) { return IsSerializable(); } return false; } } protected virtual bool IsSerializable() { return true; } public DynamicProperty(Type type, int instance) { this.type = type; this.instance = instance; } public virtual void Serialize(FileStream fs) { fs.WriteByte((byte)type); int index = 0; BitHelper.WriteBytes(instance, buffer, ref index); fs.Write(buffer); } } internal class SnapshotManager : MonoBehaviour { public struct rAgent : ITransform { private Agent agent; public bool active => (Object)(object)agent != (Object)null; public byte dimensionIndex => (byte)agent.m_dimensionIndex; public Vector3 position => ((Component)agent).transform.position; public Quaternion rotation => ((Component)agent).transform.rotation; public float scale => 0f; public rAgent(Agent agent) { this.agent = agent; } } public interface ITransform { bool active { get; } byte dimensionIndex { get; } Vector3 position { get; } Quaternion rotation { get; } float scale { get; } } public class DynamicObject { public bool remove; public ITransform transform; public readonly int instance; public Vector3 oldPosition = Vector3.zero; public Quaternion oldRotation = Quaternion.identity; public float oldScale; public const float threshold = 50f; public const int SizeOf = 27; public const int SizeOfHalf = 21; private static byte[] buffer = new byte[27]; public bool Serializable { get { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) if (!remove && transform.active) { if (!(transform.position != oldPosition) && !(transform.rotation != oldRotation)) { return transform.scale != oldScale; } return true; } return false; } } public DynamicObject(int instance, ITransform transform) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) this.instance = instance; this.transform = transform; } public void Serialize(FileStream fs) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) int index = 0; Vector3 val = transform.position - oldPosition; if (((Vector3)(ref val)).sqrMagnitude > 2500f) { BitHelper.WriteBytes((byte)1, buffer, ref index); BitHelper.WriteBytes(instance, buffer, ref index); BitHelper.WriteBytes(transform.position, buffer, ref index); if (float.IsNaN(transform.rotation.x) || float.IsNaN(transform.rotation.y) || float.IsNaN(transform.rotation.z) || float.IsNaN(transform.rotation.w)) { BitHelper.WriteHalf(Quaternion.identity, buffer, ref index); APILogger.Debug("Dynamic rotation had NaN component."); } else { BitHelper.WriteHalf(transform.rotation, buffer, ref index); } BitHelper.WriteHalf(transform.scale, buffer, ref index); BitHelper.WriteBytes(transform.dimensionIndex, buffer, ref index); fs.Write(buffer, 0, 27); } else { BitHelper.WriteBytes((byte)0, buffer, ref index); BitHelper.WriteBytes(instance, buffer, ref index); BitHelper.WriteHalf(transform.position - oldPosition, buffer, ref index); if (float.IsNaN(transform.rotation.x) || float.IsNaN(transform.rotation.y) || float.IsNaN(transform.rotation.z) || float.IsNaN(transform.rotation.w)) { BitHelper.WriteHalf(Quaternion.identity, buffer, ref index); APILogger.Debug("Dynamic rotation had NaN component."); } else { BitHelper.WriteHalf(transform.rotation, buffer, ref index); } BitHelper.WriteHalf(transform.scale, buffer, ref index); BitHelper.WriteBytes(transform.dimensionIndex, buffer, ref index); fs.Write(buffer, 0, 21); } oldPosition = transform.position; oldRotation = transform.rotation; oldScale = transform.scale; } } private static GameObject? obj = null; public static SnapshotManager? instance = null; public static FileStream? fs = null; public static bool active = false; private float tickRate = 0.2f; private float timer; private long start; private List<GameplayEvent> events = new List<GameplayEvent>(100); private List<DynamicObject> _dynamic = new List<DynamicObject>(100); private List<DynamicObject> dynamic = new List<DynamicObject>(100); public Dictionary<int, DynamicObject> mapOfDynamics = new Dictionary<int, DynamicObject>(); private List<DynamicProperty> _dynamicProp = new List<DynamicProperty>(100); private List<DynamicProperty> dynamicProp = new List<DynamicProperty>(100); public Dictionary<int, DynamicProperty> mapOfDynamicProps = new Dictionary<int, DynamicProperty>(); public static Action? OnTick; private static byte[] buffer = new byte[4]; public static string fullpath = "./replay.gtfo"; public long Now => Raudy.Now - start; private void Update() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if (active) { DRAMA_State currentStateEnum = DramaManager.CurrentStateEnum; float num = ((currentStateEnum - 5 > 3) ? 0.2f : 0.05f); if (tickRate != num) { timer = 0f; tickRate = num; } timer += Time.deltaTime; if (timer > tickRate) { timer = 0f; Tick(); } } } public static void AddEvent(GameplayEvent.Type type, ISerializable? e) { if ((Object)(object)instance == (Object)null) { APILogger.Error("Tried to add an event before snapshotmanager was ready."); } else { instance.events.Add(new GameplayEvent(instance.Now, type, e)); } } public static void AddDynamicObject(DynamicObject obj) { if ((Object)(object)instance == (Object)null) { APILogger.Error("Tried to add a dynamic object before snapshotmanager was ready."); return; } instance.dynamic.Add(obj); instance.mapOfDynamics.Add(obj.instance, obj); } public static void RemoveDynamicObject(int instanceID) { if ((Object)(object)instance == (Object)null) { APILogger.Error("Tried to remove a dynamic object before snapshotmanager was ready."); } else if (instance.mapOfDynamics.ContainsKey(instanceID)) { instance.mapOfDynamics[instanceID].remove = true; instance.mapOfDynamics.Remove(instanceID); } } public static void RemoveDynamicObject(DynamicObject obj) { RemoveDynamicObject(obj.instance); } public static void AddDynamicProperty(DynamicProperty obj) { if ((Object)(object)instance == (Object)null) { APILogger.Error("Tried to add a dynamic property before snapshotmanager was ready."); return; } instance.dynamicProp.Add(obj); instance.mapOfDynamicProps.Add(obj.instance, obj); } public static void RemoveDynamicProperty(int instanceID) { if ((Object)(object)instance == (Object)null) { APILogger.Error("Tried to remove a dynamic property before snapshotmanager was ready."); } else if (instance.mapOfDynamicProps.ContainsKey(instanceID)) { instance.mapOfDynamicProps[instanceID].remove = true; instance.mapOfDynamicProps.Remove(instanceID); } } public static void RemoveDynamicProperty(DynamicProperty obj) { RemoveDynamicProperty(obj.instance); } private void Tick() { if (!active || fs == null) { return; } OnTick?.Invoke(); int num = (ushort)dynamic.Where((DynamicObject d) => d.Serializable).Count(); int num2 = (ushort)dynamicProp.Where((DynamicProperty d) => d.Serializable).Count(); if (num2 == 0 && num == 0 && events.Count == 0) { return; } int index = 0; long now = Now; BitHelper.WriteBytes((uint)now, buffer, ref index); fs.Write(buffer, 0, 4); index = 0; BitHelper.WriteBytes((ushort)events.Count, buffer, ref index); fs.Write(buffer, 0, 2); for (int i = 0; i < events.Count; i++) { GameplayEvent gameplayEvent = events[i]; index = 0; BitHelper.WriteBytes((byte)gameplayEvent.type, buffer, ref index); BitHelper.WriteBytes((ushort)(now - gameplayEvent.timestamp), buffer, ref index); fs.Write(buffer, 0, 3); if (gameplayEvent.detail != null) { gameplayEvent.detail.Serialize(fs); } } index = 0; int num3 = 0; BitHelper.WriteBytes(num, buffer, ref index); fs.Write(buffer, 0, 2); _dynamic.Clear(); for (int j = 0; j < dynamic.Count; j++) { DynamicObject dynamicObject = dynamic[j]; if (dynamicObject.Serializable) { num3++; dynamicObject.Serialize(fs); } if (!dynamicObject.remove) { _dynamic.Add(dynamicObject); } } if (num3 != num) { APILogger.Error($"Number of serialized and nSerializable don't match: {num3} {num}"); } List<DynamicObject> list = dynamic; dynamic = _dynamic; _dynamic = list; index = 0; num3 = 0; BitHelper.WriteBytes(num2, buffer, ref index); fs.Write(buffer, 0, 2); _dynamicProp.Clear(); for (int k = 0; k < dynamicProp.Count; k++) { DynamicProperty dynamicProperty = dynamicProp[k]; if (dynamicProperty.Serializable) { num3++; dynamicProperty.Serialize(fs); } if (!dynamicProperty.remove) { _dynamicProp.Add(dynamicProperty); } } if (num3 != num2) { APILogger.Error($"Number of serialized and nSerializableProps don't match: {num3} {num2}"); } List<DynamicProperty> list2 = dynamicProp; dynamicProp = _dynamicProp; _dynamicProp = list2; fs.Flush(); events.Clear(); } public static void Setup() { OnTick = (Action)Delegate.Combine(OnTick, new Action(ReplayRecorder.Player.Player.OnTick)); } public static void Init() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)instance != (Object)null) { APILogger.Error("Instance has already started, this should not happen"); return; } active = true; obj = new GameObject(); instance = obj.AddComponent<SnapshotManager>(); instance.start = Raudy.Now; pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData(); string shortName = GameDataBlockBase<RundownDataBlock>.GetBlock(Global.RundownIdToLoad).GetExpeditionData(activeExpeditionData.tier, activeExpeditionData.expeditionIndex).GetShortName(activeExpeditionData.expeditionIndex); DateTime now = DateTime.Now; string text = string.Format(ConfigManager.ReplayFileName, shortName, now); string text2 = ConfigManager.ReplayFolder; if (text.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) { text = "replay"; } if (!Directory.Exists(text2)) { text2 = "./"; } fullpath = Path.Combine(text2, text); fs = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.Read); ReplayRecorder.Player.Player.Init(); } public static void Dispose() { active = false; if (fs == null) { APILogger.Warn("Filestream was never started, this should not happen."); } else { fs.Dispose(); fs = null; } Network.SendReplay(File.ReadAllBytes(fullpath)); if ((Object)(object)obj == (Object)null) { APILogger.Debug("Snapshot manager has already been destroyed!"); return; } APILogger.Debug("Disposing snapshot manager..."); instance = null; Object.Destroy((Object)(object)obj); obj = null; } } } namespace ReplayRecorder.Player { internal struct rPlayer : ISerializable { private byte slot; public rPlayer(PlayerAgent agent) { slot = (byte)agent.PlayerSlotIndex; } public void Serialize(FileStream fs) { fs.WriteByte(slot); } } internal class rPlayerAgent { public PlayerAgent agent; public ItemEquippable? lastEquipped; public SNet_Player owner; public int instanceID; public rPlayerAgent(PlayerAgent agent) { this.agent = agent; owner = agent.Owner; instanceID = ((Object)agent).GetInstanceID(); lastEquipped = null; } } internal static class Player { public struct PlayerJoin : ISerializable { private rPlayerAgent player; private byte slot; private const int SizeOf = 15; private byte[] buffer; public PlayerJoin(rPlayerAgent player) { buffer = new byte[15]; this.player = player; slot = (byte)player.agent.PlayerSlotIndex; } public void Serialize(FileStream fs) { byte[] bytes = Encoding.UTF8.GetBytes(player.owner.NickName); int num = 15 + bytes.Length; if (buffer.Length < num) { buffer = new byte[num]; } int index = 0; BitHelper.WriteBytes(player.owner.Lookup, buffer, ref index); BitHelper.WriteBytes(player.instanceID, buffer, ref index); BitHelper.WriteBytes(slot, buffer, ref index); BitHelper.WriteBytes(bytes, buffer, ref index); fs.Write(buffer, 0, num); } } public struct PlayerLeave : ISerializable { private rPlayerAgent player; public const int SizeOf = 12; private byte[] buffer; public PlayerLeave(rPlayerAgent player) { buffer = new byte[12]; this.player = player; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(player.owner.Lookup, buffer, ref index); BitHelper.WriteBytes(player.instanceID, buffer, ref index); fs.Write(buffer); } } public struct rPlayerRevive : ISerializable { private byte player; private byte source; public rPlayerRevive(PlayerAgent player, PlayerAgent source) { this.player = (byte)player.PlayerSlotIndex; this.source = (byte)source.PlayerSlotIndex; } public void Serialize(FileStream fs) { fs.WriteByte(player); fs.WriteByte(source); } } public struct rPlayerWield : ISerializable { private byte slot; private byte item; private const int SizeOf = 2; private byte[] buffer; public rPlayerWield(PlayerAgent player, byte item) { buffer = new byte[2]; slot = (byte)player.PlayerSlotIndex; this.item = item; } public void Serialize(FileStream fs) { fs.WriteByte(slot); fs.WriteByte(item); } } private static List<PlayerAgent> buffer = new List<PlayerAgent>(); private static List<rPlayerAgent> _players = new List<rPlayerAgent>(); private static List<rPlayerAgent> playerList = new List<rPlayerAgent>(); public static Dictionary<int, rPlayerAgent> players = new Dictionary<int, rPlayerAgent>(); public static void OnPlayerDead(PlayerAgent player) { APILogger.Debug(player.Owner.NickName + " has died."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerDead, new rPlayer(player)); } public static void OnPlayerRevive(PlayerAgent player, PlayerAgent source) { APILogger.Debug(player.Owner.NickName + " was revived by " + source.Owner.NickName + "."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerRevive, new rPlayerRevive(player, source)); } public static void CheckPlayerWield() { foreach (rPlayerAgent player in playerList) { ItemEquippable wieldedItem = player.agent.Inventory.WieldedItem; if ((Object)(object)wieldedItem != (Object)(object)player.lastEquipped) { player.lastEquipped = wieldedItem; if ((Object)(object)player.lastEquipped != (Object)null) { OnPlayerWield(player.agent, player.lastEquipped); } } } } public static void OnPlayerWield(PlayerAgent player, ItemEquippable item) { APILogger.Debug($"{player.Owner.NickName} is wielding {((Item)item).PublicName} [{item.ArchetypeID}]"); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerWield, new rPlayerWield(player, GTFOSpecification.GetItem(item.ArchetypeID))); } public static void Init() { APILogger.Debug("Initializing..."); playerList.Clear(); players.Clear(); Enumerator<PlayerAgent> enumerator = PlayerManager.PlayerAgentsInLevel.GetEnumerator(); while (enumerator.MoveNext()) { PlayerAgent current = enumerator.Current; rPlayerAgent rPlayerAgent2 = new rPlayerAgent(current); playerList.Add(rPlayerAgent2); players.Add(rPlayerAgent2.instanceID, rPlayerAgent2); APILogger.Debug(current.Owner.NickName + " has joined."); SnapshotManager.AddEvent(GameplayEvent.Type.AddPlayer, new PlayerJoin(rPlayerAgent2)); SnapshotManager.AddDynamicObject(new SnapshotManager.DynamicObject(rPlayerAgent2.instanceID, new SnapshotManager.rAgent((Agent)(object)current))); } } public static void SpawnPlayer(PlayerAgent agent) { PlayerAgent agent2 = agent; rPlayerAgent rPlayerAgent2 = playerList.Find((rPlayerAgent p) => p.owner.Lookup == agent2.Owner.Lookup); if (rPlayerAgent2 != null) { APILogger.Debug("(SpawnPlayer) " + agent2.Owner.NickName + " was replaced by spawned agent."); SnapshotManager.AddEvent(GameplayEvent.Type.RemovePlayer, new PlayerLeave(rPlayerAgent2)); SnapshotManager.RemoveDynamicObject(rPlayerAgent2.instanceID); playerList.Remove(rPlayerAgent2); players.Remove(rPlayerAgent2.instanceID); } else { APILogger.Debug("(SpawnPlayer) " + agent2.Owner.NickName + " has joined."); } rPlayerAgent rPlayerAgent3 = new rPlayerAgent(agent2); SnapshotManager.AddEvent(GameplayEvent.Type.AddPlayer, new PlayerJoin(rPlayerAgent3)); SnapshotManager.AddDynamicObject(new SnapshotManager.DynamicObject(rPlayerAgent3.instanceID, new SnapshotManager.rAgent((Agent)(object)agent2))); playerList.Add(rPlayerAgent3); players.Add(rPlayerAgent3.instanceID, rPlayerAgent3); } public static void DespawnPlayer(PlayerAgent agent) { PlayerAgent agent2 = agent; rPlayerAgent rPlayerAgent2 = playerList.Find((rPlayerAgent p) => p.owner.Lookup == agent2.Owner.Lookup); if (rPlayerAgent2 != null) { APILogger.Debug(agent2.Owner.NickName + " has left."); SnapshotManager.AddEvent(GameplayEvent.Type.RemovePlayer, new PlayerLeave(rPlayerAgent2)); SnapshotManager.RemoveDynamicObject(rPlayerAgent2.instanceID); players.Remove(rPlayerAgent2.instanceID); playerList.Remove(rPlayerAgent2); } } private static void CheckPlayersJoined() { buffer.Clear(); Enumerator<PlayerAgent> enumerator = PlayerManager.PlayerAgentsInLevel.GetEnumerator(); while (enumerator.MoveNext()) { PlayerAgent current = enumerator.Current; buffer.Add(current); } foreach (rPlayerAgent player in playerList) { SNet_Player owner = player.owner; if (!buffer.Any((PlayerAgent p) => p.Owner.Lookup == owner.Lookup)) { APILogger.Debug("(Tick) " + owner.NickName + " has left."); SnapshotManager.AddEvent(GameplayEvent.Type.RemovePlayer, new PlayerLeave(player)); SnapshotManager.RemoveDynamicObject(player.instanceID); players.Remove(player.instanceID); } } _players.Clear(); foreach (PlayerAgent item in buffer) { SNet_Player owner2 = item.Owner; rPlayerAgent rPlayerAgent2 = playerList.Find((rPlayerAgent p) => p.owner.Lookup == owner2.Lookup); if (rPlayerAgent2 == null) { APILogger.Debug("(Tick) " + owner2.NickName + " has joined."); rPlayerAgent2 = new rPlayerAgent(item); SnapshotManager.AddEvent(GameplayEvent.Type.AddPlayer, new PlayerJoin(rPlayerAgent2)); SnapshotManager.AddDynamicObject(new SnapshotManager.DynamicObject(rPlayerAgent2.instanceID, new SnapshotManager.rAgent((Agent)(object)item))); players.Add(rPlayerAgent2.instanceID, rPlayerAgent2); } _players.Add(rPlayerAgent2); } List<rPlayerAgent> list = _players; _players = playerList; playerList = list; } public static void OnTick() { CheckPlayersJoined(); CheckPlayerWield(); } } internal class PlayerDamage { public struct rPlayerDamage : ISerializable { private byte player; private int? source; private float damage; public const int SizeOf = 7; public const int SizeOfNoSource = 3; private byte[] buffer; public rPlayerDamage(PlayerAgent player, float damage, int source) { this.source = null; buffer = new byte[7]; this.player = (byte)player.PlayerSlotIndex; this.source = source; this.damage = damage; } public rPlayerDamage(PlayerAgent player, float damage) { source = null; buffer = new byte[7]; this.player = (byte)player.PlayerSlotIndex; this.damage = damage; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(player, buffer, ref index); BitHelper.WriteHalf(damage, buffer, ref index); if (source.HasValue) { BitHelper.WriteBytes(source.Value, buffer, ref index); fs.Write(buffer); } else { fs.Write(buffer, 0, 3); } } } public struct rFriendlyFire : ISerializable { private byte player; private byte source; private float damage; public const int SizeOf = 4; private byte[] buffer; public rFriendlyFire(PlayerAgent player, float damage, PlayerAgent source) { buffer = new byte[4]; this.player = (byte)player.PlayerSlotIndex; this.source = (byte)source.PlayerSlotIndex; this.damage = damage; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(player, buffer, ref index); BitHelper.WriteHalf(damage, buffer, ref index); BitHelper.WriteBytes(source, buffer, ref index); fs.Write(buffer); } } public struct rPlayerDodge : ISerializable { private byte player; private int source; public const int SizeOf = 5; private byte[] buffer; public rPlayerDodge(PlayerAgent player, int source) { buffer = new byte[5]; this.player = (byte)player.PlayerSlotIndex; this.source = source; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(player, buffer, ref index); BitHelper.WriteBytes(source, buffer, ref index); fs.Write(buffer); } } public static void OnFallDamage(PlayerAgent player, float damage) { APILogger.Debug($"{player.Owner.NickName} took {damage} fall damage."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerFallDamage, new rPlayerDamage(player, damage)); } public static void OnMeleeDamage(PlayerAgent player, float damage, EnemyAgent source) { int instanceID = ((Object)source).GetInstanceID(); if (!Enemy.enemies.ContainsKey(instanceID)) { APILogger.Error("(PlayerMeleeDamage) Enemy instance was not found."); return; } APILogger.Debug($"{player.Owner.NickName} took {damage} melee damage from [{instanceID}]."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerMeleeDamage, new rPlayerDamage(player, damage, instanceID)); } public static void OnTongueDamage(PlayerAgent player, float damage, EnemyAgent source, MovingEnemyTentacleBase tongue) { int instanceID = ((Object)source).GetInstanceID(); if (!Enemy.enemies.ContainsKey(instanceID)) { APILogger.Error("(PlayerMeleeDamage) Enemy instance was not found."); return; } APILogger.Debug($"{player.Owner.NickName} took {damage} tongue damage from [{instanceID}]."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerTongueDamage, new rPlayerDamage(player, damage, instanceID)); SnapshotManager.AddEvent(GameplayEvent.Type.SetTongue, new Enemy.TongueEvent(tongue)); } public static void OnPelletDamage(PlayerAgent player, float damage, EnemyAgent source) { int instanceID = ((Object)source).GetInstanceID(); if (!Enemy.enemies.ContainsKey(instanceID)) { APILogger.Error("(PlayerMeleeDamage) Enemy instance was not found."); return; } APILogger.Debug($"{player.Owner.NickName} took {damage} pellet damage from [{instanceID}]."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerPelletDamage, new rPlayerDamage(player, damage, instanceID)); } public static void OnBulletDamage(PlayerAgent player, float damage, PlayerAgent source) { APILogger.Debug($"{player.Owner.NickName} took {damage} bullet damage from [{source.Owner.NickName}]."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerBulletDamage, new rFriendlyFire(player, damage, source)); } public static void OnMineDamage(PlayerAgent player, float damage, PlayerAgent source) { APILogger.Debug($"{player.Owner.NickName} took {damage} mine damage from [{source.Owner.NickName}]."); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerMineDamage, new rFriendlyFire(player, damage, source)); } public static void OnPelletDodge(PlayerAgent player, int source) { APILogger.Debug($"{player.Owner.NickName} dodged projectile from [{source}]"); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerPelletDodge, new rPlayerDodge(player, source)); } public static void OnTongueDodge(PlayerAgent player, int source, MovingEnemyTentacleBase tongue) { APILogger.Debug($"{player.Owner.NickName} dodged tongue from [{source}]"); SnapshotManager.AddEvent(GameplayEvent.Type.PlayerTongueDodge, new rPlayerDodge(player, source)); SnapshotManager.AddEvent(GameplayEvent.Type.SetTongue, new Enemy.TongueEvent(tongue)); } } internal class PlayerHealth { public struct rPlayerQuantity : ISerializable { private byte player; private float value; public const int SizeOf = 3; private byte[] buffer; public rPlayerQuantity(PlayerAgent player, float value) { buffer = new byte[3]; this.player = (byte)player.PlayerSlotIndex; this.value = value; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(player, buffer, ref index); BitHelper.WriteHalf(value, buffer, ref index); fs.Write(buffer); } } public static void OnHealthChange(PlayerAgent player, float value) { } public static void OnInfectionChange(PlayerAgent player, float value) { } } internal class rSentry : ISerializable, SnapshotManager.ITransform { public SentryGunInstance sentry; public int instance; public byte slot; private Vector3 pos; public SNet_Player player; public const int SizeOf = 5; private static byte[] buffer = new byte[5]; public bool active => sentry.m_firing != null; public byte dimensionIndex => (byte)((Agent)((Item)sentry).Owner).m_dimensionIndex; public Vector3 position => pos; public Quaternion rotation => Quaternion.LookRotation(sentry.m_firing.MuzzleAlign.forward); public float scale => 0f; public rSentry(SNet_Player player, SentryGunInstance sentry, Vector3 pos) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) this.player = player; this.sentry = sentry; instance = ((Object)sentry).GetInstanceID(); slot = (byte)player.PlayerSlotIndex(); this.pos = pos; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(slot, buffer, ref index); BitHelper.WriteBytes(instance, buffer, ref index); fs.Write(buffer); } } internal static class PlayerSentry { public static string? sentryName = null; public static bool sentryShot = false; public static Dictionary<ulong, rSentry> sentries = new Dictionary<ulong, rSentry>(); private static Dictionary<int, rSentry> instances = new Dictionary<int, rSentry>(); public static void SpawnSentry(SNet_Player player, SentryGunInstance sentry, Vector3 pos) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) APILogger.Debug(player.NickName + " spawned a sentry."); int instanceID = ((Object)sentry).GetInstanceID(); rSentry rSentry2 = new rSentry(player, sentry, pos); sentries.Add(rSentry2.player.Lookup, rSentry2); instances.Add(instanceID, rSentry2); SnapshotManager.AddEvent(GameplayEvent.Type.SpawnSentry, rSentry2); SnapshotManager.AddDynamicObject(new SnapshotManager.DynamicObject(instanceID, rSentry2)); } public static void DespawnSentry(SentryGunInstance sentry) { int instanceID = ((Object)sentry).GetInstanceID(); if (instances.ContainsKey(instanceID)) { rSentry rSentry2 = instances[instanceID]; APILogger.Debug("Sentry despawned by " + rSentry2.player.NickName + "."); sentries.Remove(rSentry2.player.Lookup); instances.Remove(instanceID); SnapshotManager.AddEvent(GameplayEvent.Type.DespawnSentry, new rInstance(instanceID)); SnapshotManager.RemoveDynamicObject(instanceID); } else { APILogger.Error("Sentry does not exist to despawn."); } } public static void Reset() { instances.Clear(); sentries.Clear(); } } } namespace ReplayRecorder.Player.Patches { [HarmonyPatch] internal class PlayerDamagePatches { private static bool wasTargeting; private static ulong player; private static MovingEnemyTentacleBase? tongue; [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveFallDamage")] [HarmonyPrefix] private static void ReceiveFallDamage(Dam_PlayerDamageBase __instance, pMiniDamageData data) { PlayerDamage.OnFallDamage(__instance.Owner, ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)__instance).HealthMax)); } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveTentacleAttackDamage")] [HarmonyPrefix] private static void Prefix_ReceiveTentacleAttackDamage(Dam_PlayerDamageBase __instance, pMediumDamageData data) { //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) Agent val = default(Agent); if (((pAgent)(ref data.source)).TryGet(ref val)) { EnemyAgent val2 = ((Il2CppObjectBase)val).TryCast<EnemyAgent>(); if ((Object)(object)val2 == (Object)null) { AgentType type = val.m_type; APILogger.Debug("Could not find EnemyAgent, damage was done by agent of type: " + ((object)(AgentType)(ref type)).ToString() + "."); return; } float num = ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)__instance).HealthMax); num = AgentModifierManager.ApplyModifier(val, (AgentModifier)200, num); if ((Object)(object)tongue != (Object)null) { PlayerDamage.OnTongueDamage(__instance.Owner, num, val2, tongue); } else if (SNet.IsMaster) { APILogger.Error("Could not find tongue player was hit by as master, this should not happen."); } } tongue = null; } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveMeleeDamage")] [HarmonyPrefix] private static void ReceiveMeleeDamage(Dam_PlayerDamageBase __instance, pFullDamageData data) { //IL_0026: 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) Agent val = default(Agent); if (((pAgent)(ref data.source)).TryGet(ref val)) { EnemyAgent val2 = ((Il2CppObjectBase)val).TryCast<EnemyAgent>(); if ((Object)(object)val2 == (Object)null) { AgentType type = val.m_type; APILogger.Debug("Could not find EnemyAgent, damage was done by agent of type: " + ((object)(AgentType)(ref type)).ToString() + "."); } else { float num = ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)__instance).DamageMax); num = AgentModifierManager.ApplyModifier(val, (AgentModifier)200, num); PlayerDamage.OnMeleeDamage(__instance.Owner, num, val2); } } } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveShooterProjectileDamage")] [HarmonyPrefix] private static void ReceivePelletDamage(Dam_PlayerDamageBase __instance, pMediumDamageData data) { //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) Agent val = default(Agent); if (SNet.IsMaster && ((pAgent)(ref data.source)).TryGet(ref val)) { EnemyAgent val2 = ((Il2CppObjectBase)val).TryCast<EnemyAgent>(); if ((Object)(object)val2 == (Object)null) { AgentType type = val.m_type; APILogger.Debug("Could not find EnemyAgent, damage was done by agent of type: " + ((object)(AgentType)(ref type)).ToString() + "."); } else { float num = ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)__instance).HealthMax); num = AgentModifierManager.ApplyModifier(val, (AgentModifier)70, num); PlayerDamage.OnPelletDamage(__instance.Owner, num, val2); } } } [HarmonyPatch(typeof(ProjectileTargeting), "Update")] [HarmonyPrefix] private static void Prefix_Update(ProjectileTargeting __instance) { if (!SNet.IsMaster) { return; } int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (Enemy.pellets.ContainsKey(instanceID)) { wasTargeting = false; if (__instance.m_isTargeting && (Object)(object)__instance.m_playerTarget != (Object)null) { wasTargeting = true; player = __instance.m_playerTarget.Owner.Lookup; } } } [HarmonyPatch(typeof(ProjectileTargeting), "Update")] [HarmonyPostfix] private static void Postfix_Update(ProjectileTargeting __instance) { if (!SNet.IsMaster) { return; } int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (Enemy.pellets.ContainsKey(instanceID) && !__instance.m_isTargeting && wasTargeting) { PlayerAgent playerTarget = __instance.m_playerTarget; if ((Object)(object)__instance.m_playerTarget != (Object)null && playerTarget.Owner.Lookup == player) { PlayerDamage.OnPelletDodge(playerTarget, Enemy.pellets[instanceID].instance); } } } [HarmonyPatch(typeof(MovingEnemyTentacleBase), "OnAttackIsOut")] [HarmonyPrefix] private static void OnAttackIsOut(MovingEnemyTentacleBase __instance) { //IL_0049: 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_0055: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (!SNet.IsMaster) { return; } tongue = __instance; PlayerAgent playerTarget = __instance.PlayerTarget; bool flag = __instance.CheckTargetInAttackTunnel(); if ((Object)(object)playerTarget != (Object)null && ((Dam_SyncedDamageBase)playerTarget.Damage).IsSetup) { bool flag2; if (__instance.m_owner.EnemyBalancingData.UseTentacleTunnelCheck) { flag2 = flag; } else { Vector3 tipPos = __instance.GetTipPos(); Vector3 val = ((Agent)playerTarget).TentacleTarget.position - tipPos; flag2 = ((Vector3)(ref val)).magnitude < __instance.m_owner.EnemyBalancingData.TentacleAttackDamageRadiusIfNoTunnelCheck; } if (!flag2) { int instanceID = ((Object)__instance.m_owner).GetInstanceID(); PlayerDamage.OnTongueDodge(playerTarget, instanceID, tongue); } } } } [HarmonyPatch] internal class PlayerHealthPatches { } [HarmonyPatch] internal class PlayerPatches { [HarmonyPatch(typeof(PlayerSync), "OnSpawn")] [HarmonyPostfix] private static void OnSpawn(PlayerSync __instance) { if (SnapshotManager.active) { Player.SpawnPlayer(__instance.m_agent); } } [HarmonyPatch(typeof(PlayerSync), "OnDespawn")] [HarmonyPostfix] private static void OnDespawn(PlayerSync __instance) { if (SnapshotManager.active) { Player.DespawnPlayer(__instance.m_agent); } } [HarmonyPatch(typeof(Dam_PlayerDamageBase), "ReceiveSetDead")] [HarmonyPostfix] private static void OnPlayerDead(Dam_PlayerDamageBase __instance, pSetDeadData data) { if (SnapshotManager.active) { Player.OnPlayerDead(__instance.Owner); } } [HarmonyPatch(typeof(AgentReplicatedActions), "DoPlayerRevive")] [HarmonyPostfix] private static void DoPlayerRevive(pPlayerReviveAction data) { PlayerAgent val = default(PlayerAgent); if (((pPlayerAgent)(ref data.TargetPlayer)).TryGet(ref val) && !((Agent)val).Alive) { PlayerAgent val2 = default(PlayerAgent); if (((pPlayerAgent)(ref data.SourcePlayer)).TryGet(ref val2)) { Player.OnPlayerRevive(val, val2); APILogger.Debug(val.Owner.NickName + " was revived by " + val2.Owner.NickName); } else { APILogger.Debug("Unable to get source player, this should not happen."); } } } } [HarmonyPatch] internal class PlayerSentryPatches { private static bool CompatabilityLayer_HelAutoSentryFix_Installed; private static bool shotgunSentryShot; [HarmonyPatch(typeof(SentryGunInstance), "OnSpawn")] [HarmonyPostfix] private static void OnSpawn(SentryGunInstance __instance, pGearSpawnData spawnData) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) pPlayer owner = spawnData.owner; SNet_Player player = default(SNet_Player); if (((pPlayer)(ref owner)).TryGetPlayer(ref player)) { PlayerSentry.SpawnSentry(player, __instance, spawnData.position); } else { APILogger.Error("Sentry had no player as owner."); } } [HarmonyPatch(typeof(SentryGunInstance), "OnDespawn")] [HarmonyPostfix] private static void OnDespawn(SentryGunInstance __instance) { PlayerSentry.DespawnSentry(__instance); } public static void CompatabilityLayer_HelAutoSentryFix() { CompatabilityLayer_HelAutoSentryFix_Installed = true; } private static void _Prefix_SentryGunFire(SentryGunInstance_Firing_Bullets __instance, bool doDamage, bool targetIsTagged) { if (doDamage) { PlayerSentry.sentryName = LocalizedText.op_Implicit(__instance.ArchetypeData.PublicName); SentryGunInstance component = ((Component)__instance).GetComponent<SentryGunInstance>(); if ((Object)(object)component != (Object)null) { PlayerSentry.sentryName = ((Item)component).PublicName; } else { APILogger.Debug("Could not find sentry gun instance, this should not happen."); } PlayerSentry.sentryShot = true; } } private static void _Postfix_SentryGunFire() { PlayerSentry.sentryName = null; PlayerSentry.sentryShot = false; } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "FireBullet")] [HarmonyPrefix] private static void Prefix_SentryGunFiringBullet(SentryGunInstance_Firing_Bullets __instance, bool doDamage, bool targetIsTagged) { if (!CompatabilityLayer_HelAutoSentryFix_Installed) { _Prefix_SentryGunFire(__instance, doDamage, targetIsTagged); } } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "FireBullet")] [HarmonyPostfix] private static void Postfix_SentryGunFiringBullet() { if (!CompatabilityLayer_HelAutoSentryFix_Installed) { _Postfix_SentryGunFire(); } } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireShotgunSemi")] [HarmonyPrefix] private static void Prefix_ShotgunSentryFiring(SentryGunInstance_Firing_Bullets __instance, bool isMaster, bool targetIsTagged) { if (!CompatabilityLayer_HelAutoSentryFix_Installed && isMaster && Clock.Time > __instance.m_fireBulletTimer) { shotgunSentryShot = true; _Postfix_SentryGunFire(); } } [HarmonyPatch(typeof(SentryGunInstance_Firing_Bullets), "UpdateFireShotgunSemi")] [HarmonyPostfix] private static void Postfix_ShotgunSentryFiring() { if (!CompatabilityLayer_HelAutoSentryFix_Installed && shotgunSentryShot) { shotgunSentryShot = false; _Postfix_SentryGunFire(); } } } } namespace ReplayRecorder.Mines { internal class rMine : ISerializable { public enum Type { Mine, Cfoam } public byte owner; public byte type; public byte dimensionIndex; public int instance; public Vector3 position; public Quaternion rotation; public const int SizeOf = 26; private static byte[] buffer = new byte[26]; public rMine(SNet_Player owner, int instance, Type type, Vector3 position, Quaternion rotation) { //IL_002c: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) this.owner = (byte)owner.PlayerSlotIndex(); this.type = (byte)type; dimensionIndex = (byte)((Agent)PlayerManager.PlayerAgentsInLevel[(int)this.owner]).m_dimensionIndex; this.instance = instance; this.position = position; this.rotation = rotation; } public void Serialize(FileStream fs) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) int index = 0; BitHelper.WriteBytes(owner, buffer, ref index); BitHelper.WriteBytes(type, buffer, ref index); BitHelper.WriteBytes(dimensionIndex, buffer, ref index); BitHelper.WriteBytes(instance, buffer, ref index); BitHelper.WriteBytes(position, buffer, ref index); BitHelper.WriteHalf(rotation, buffer, ref index); fs.Write(buffer); } } internal static class Mine { public struct rTripLine : ISerializable { public int instance; public float length; public const int SizeOf = 6; private static byte[] buffer = new byte[6]; public rTripLine(int instance, float length) { this.instance = instance; this.length = length; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(instance, buffer, ref index); BitHelper.WriteHalf(length, buffer, ref index); fs.Write(buffer); } } private struct rExplodeMine : ISerializable { private int instance; private byte player; public const int SizeOf = 5; private static byte[] buffer = new byte[5]; public rExplodeMine(int instance, byte player) { this.instance = instance; this.player = player; } public void Serialize(FileStream fs) { int index = 0; BitHelper.WriteBytes(instance, buffer, ref index); BitHelper.WriteBytes(player, buffer, ref index); fs.Write(buffer); } } public static Dictionary<int, rMine> mines = new Dictionary<int, rMine>(); public static void TripLine(int instance, float length) { APILogger.Debug($"Mine instance length updated [{instance}]"); SnapshotManager.AddEvent(GameplayEvent.Type.MineTripLine, new rTripLine(instance, length)); } public static void SpawnMine(SNet_Player owner, int instance, rMine.Type type, Vector3 position, Quaternion rotation) { //IL_0052: 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) APILogger.Debug($"Mine instance spawned by {owner.NickName} - [{instance}]"); rMine rMine2 = new rMine(owner, instance, type, position, rotation); mines.Add(instance, rMine2); SnapshotManager.AddEvent(GameplayEvent.Type.SpawnMine, rMine2); } public static void DespawnMine(int instance) { APILogger.Debug($"Mine instance [{instance}] was despawned"); SnapshotManager.AddEvent(GameplayEvent.Type.DespawnMine, new rInstance(instance)); mines.Remove(instance); } public static void ExplodeMine(int instance, byte player) { APILogger.Debug($"Mine instance [{instance}] detonated"); SnapshotManager.AddEvent(GameplayEvent.Type.ExplodeMine, new rExplodeMine(instance, player)); mines.Remove(instance); } public static void Reset() { mines.Clear(); } } } namespace ReplayRecorder.Mines.Patches { [HarmonyPatch] internal class MinePatches { private static rMine? currentMine; private static float prevLength; private static PlayerAgent? player; [HarmonyPatch(typeof(MineDeployerInstance), "FixedUpdate")] [HarmonyPrefix] private static void Prefix_TripLineUpdate(MineDeployerInstance __instance) { prevLength = __instance.m_lastDetectionRange; } [HarmonyPatch(typeof(MineDeployerInstance), "FixedUpdate")] [HarmonyPostfix] private static void Postfix_TripLineUpdate(MineDeployerInstance __instance) { if (prevLength != __instance.m_lastDetectionRange) { int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (Mine.mines.ContainsKey(instanceID)) { Mine.TripLine(instanceID, __instance.m_lastDetectionRange); } else { APILogger.Error("Mine did not exist in catalogue, this should not happen."); } } } [HarmonyPatch(typeof(MineDeployerInstance), "OnSyncTripLineUpdate")] [HarmonyPrefix] private static void Prefix_TripLineSyncUpdate(MineDeployerInstance __instance, pTripLineUpdate data) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (data.lineLength != __instance.m_detection.DetectionRange) { int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (Mine.mines.ContainsKey(instanceID)) { Mine.TripLine(instanceID, data.lineLength); } else { APILogger.Error("Mine did not exist in catalogue, this should not happen."); } } } [HarmonyPatch(typeof(MineDeployerInstance), "OnSpawn")] [HarmonyPostfix] private static void Spawn(MineDeployerInstance __instance, pItemSpawnData spawnData) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); SNet_Player owner = default(SNet_Player); if (((pPlayer)(ref spawnData.owner)).TryGetPlayer(ref owner)) { APILogger.Debug($"Mine Spawn ID - {spawnData.itemData.itemID_gearCRC}"); switch (spawnData.itemData.itemID_gearCRC) { case 125u: Mine.SpawnMine(owner, instanceID, rMine.Type.Mine, spawnData.position, spawnData.rotation); break; case 139u: Mine.SpawnMine(owner, instanceID, rMine.Type.Mine, spawnData.position, spawnData.rotation); break; case 144u: Mine.SpawnMine(owner, instanceID, rMine.Type.Cfoam, spawnData.position, spawnData.rotation); break; } } } [HarmonyPatch(typeof(MineDeployerInstance), "SyncedPickup")] [HarmonyPrefix] private static void SyncedPickup(MineDeployerInstance __instance) { Mine.DespawnMine(((Object)((Component)__instance).gameObject).GetInstanceID()); } [HarmonyPatch(typeof(GenericDamageComponent), "BulletDamage")] [HarmonyPrefix] private static void Prefix_BulletDamage(float dam, Agent sourceAgent) { player = ((Il2CppObjectBase)sourceAgent).TryCast<PlayerAgent>(); } [HarmonyPatch(typeof(GenericDamageComponent), "BulletDamage")] [HarmonyPostfix] private static void Postfix_BulletDamage() { player = null; } [HarmonyPatch(typeof(MineDeployerInstance), "SyncedTrigger")] [HarmonyPrefix] private static void Prefix_SyncedTrigger(MineDeployerInstance __instance) { int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); if (Mine.mines.ContainsKey(instanceID)) { currentMine = Mine.mines[instanceID]; if ((Object)(object)player != (Object)null) { APILogger.Debug("Player triggered mine: " + player.PlayerName); Mine.ExplodeMine(instanceID, (byte)player.PlayerSlotIndex); } else { APILogger.Debug("Mine triggered without player."); Mine.ExplodeMine(instanceID, byte.MaxValue); } } else { APILogger.Error("Mine did not exist in catalogue, this should not happen."); } } [HarmonyPatch(typeof(MineDeployerInstance), "SyncedTrigger")] [HarmonyPostfix] private static void Postfix_SyncedTrigger() { currentMine = null; } } } namespace ReplayRecorder.Map { internal static class Map { public class rMap : ISerializable { public struct Location { public bool valid; public char area; public int zone; public Location(int zone, char area) { valid = false; this.area = area; this.zone = zone; valid = true; } public Location() { valid = false; valid = false; area = 'Z'; zone = 0; } } public class Surface { public Mesh mesh; public Surface(Mesh mesh) { this.mesh = mesh; } } public eDimensionIndex dimension; public Surface[] surfaces; private static byte[] buffer = new byte[3]; public rMap(eDimensionIndex dimension, Surface[] surfaces) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) this.dimension = dimension; this.surfaces = surfaces; } public void Serialize(FileStream fs) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) int index = 0; int num = 3; if (buffer.Length < num) { buffer = new byte[num]; } if (surfaces.Length > 65535) { APILogger.Error($"There were more than {65535} surfaces"); return; } BitHelper.WriteBytes((byte)dimension, buffer, ref index); BitHelper.WriteBytes((ushort)surfaces.Length, buffer, ref index); for (int i = 0; i < surfaces.Length; i++) { Surface obj = surfaces[i]; Vector3[] array = Il2CppArrayBase<Vector3>.op_Implicit((Il2CppArrayBase<Vector3>)(object)obj.mesh.vertices); int[] array2 = Il2CppArrayBase<int>.op_Implicit((Il2CppArrayBase<int>)(object)obj.mesh.triangles); if (array.Length > 65535) { APILogger.Error($"There were more than {65535} vertices"); return; } num += 6 + 12 * array.Length + 2 * array2.Length; if (buffer.Length < num) { byte[] destinationArray = new byte[num]; Array.Copy(buffer, destinationArray, buffer.Length); buffer = destinationArray; } BitHelper.WriteBytes((ushort)array.Length, buffer, ref index); BitHelper.WriteBytes((uint)array2.Length, buffer, ref index); for (int j = 0; j < array.Length; j++) { BitHelper.WriteBytes(array[j], buffer, ref index); } for (int k = 0; k < array2.Length; k++) { BitHelper.WriteBytes((ushort)array2[k], buffer, ref index); } } fs.Write(buffer, 0, num); } } public class rDoor : ISerializable { public enum Type { WeakDoor, SecurityDoor, BulkheadDoor, BulkheadDoorMain, ApexDoor } public static byte _id = 0; public Type type; public byte id; public LG_GateType size; public float healthMax; public bool isCheckpoint; public rMap.Location to; public rMap.Location from; public LG_Gate gate; public GameObject doorObj; public const int SizeOf = 23; private static byte[] buffer = new byte[23]; public rDoor(float healthMax, Type type, LG_Gate gate, GameObject doorObj) { //IL_0007: 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_0080: 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_0015: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((int)gate.Type != 0 && (int)gate.Type != 1 && (int)gate.Type != 2) { APILogger.Warn($"Weird gate type was created as a door: {gate.Type}"); } id = _id++; this.gate = gate; this.healthMax = healthMax; this.doorObj = doorObj; size = gate.Type; this.type = type; isCheckpoint = gate.IsCheckpointDoor; } public void Serialize(FileStream fs) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) int index = 0; BitHelper.WriteBytes(id, buffer, ref index); BitHelper.WriteBytes((byte)type, buffer, ref index); BitHelper.WriteBytes((byte)size, buffer, ref index); BitHelper.WriteBytes((byte)healthMax, buffer, ref index); BitHelper.WriteBytes(doorObj.transform.position, buffer, ref index); BitHelper.WriteHalf(doorObj.transform.rotation, buffer, ref index); fs.Write(buffer); } } public struct rDoorID : ISerializable { public byte id; public rDoorID(rDoor door) { id = door.id; } public void Serialize(FileStream fs) { fs.WriteByte(id); } } public struct rDoorState : ISerializable { public byte id; public byte state; public rDoorState(rDoor door, eDoorStatus state) { //IL_000c: 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_0021: Expected I4, but got Unknown id = door.id; switch (state - 10) { case 0: this.state = 1; break; case 2: this.state = 2; break; case 1: this.state = 3; break; default: this.state = 0; break; } } public void Serialize(FileStream fs) { fs.WriteByte(id); fs.WriteByte(state); } } public class rLadder : ISerializable { public static byte _id = 0; private LG_Ladder ladder; public byte id; public const int SizeOf = 22; private static byte[] buffer = new byte[22]; public Vector3 top => ladder.m_ladderTop; public float height => ladder.m_height; public Quaternion rotation => ((Component)ladder).gameObject.transform.rotation; public rLadder(LG_Ladder ladder) { id = _id++; this.ladder = ladder; } public void Serialize(FileStream fs) { //IL_0015: 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) int index = 0; BitHelper.WriteBytes(id, buffer, ref index); BitHelper.WriteBytes(top, buffer, ref index); BitHelper.WriteHalf(height, buffer, ref index); BitHelper.WriteHalf(rotation, buffer, ref index); fs.Write(buffer); } } public class rContainer : ISerializable { public static byte _id = 0; public byte id; private LG_ResourceContainer_Storage container; public int instance; public const int SizeOf = 20; private static byte[] buffer = new byte[20]; public Vector3 position => ((Component)container).transform.position; public Quaternion rotation => ((Component)container).transform.rotation; public rContainer(LG_ResourceContainer_Storage container) { id = _id++; instance = ((Object)container).GetInstanceID(); this.container = container; } public void Serialize(FileStream fs) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) int index = 0; BitHelper.WriteBytes(id, buffer, ref index); BitHelper.WriteBytes(position, buffer, ref index); BitHelper.WriteHalf(rotation, buffer, ref index); fs.Write(buffer); } } public class rTerminal : ISerializable { public static byte _id = 0; public byte id; private LG_ComputerTerminal terminal; public const int SizeOf = 20; private static byte[] buffer = new byte[20]; public Vector3 position => terminal.m_position; public Quaternion rotation => ((Component)terminal).transform.rotation; public rTerminal(LG_ComputerTerminal terminal) { id = _id++; this.terminal = terminal; } public void Serialize(FileStream fs) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) int index = 0; BitHelper.WriteBytes(id, buffer, ref index); BitHelper.WriteBytes(position, buffer, ref index); BitHelper.WriteHalf(rotation, buffer, ref index); fs.Write(buffer); } } public static Dictionary<eDimensionIndex, rMap> map = new Dictionary<eDimensionIndex, rMap>(); private static HashSet<eDimensionIndex> processed = new HashSet<eDimensionIndex>(); public static List<rDoor> doors = new List<rDoor>(); public static Dictionary<eDimensionIndex, List<rLadder>> ladders = new Dictionary<eDimensionIndex, List<rLadder>>(); public static Dictionary<eDimensionIndex, Dictionary<int, rContainer>> containers = new Dictionary<eDimensionIndex, Dictionary<int, rContainer>>(); public static Dictionary<eDimensionIndex, List<rTerminal>> terminals = new Dictionary<eDimensionIndex, List<rTerminal>>(); private static void GenerateMeshSurfaces(Dimension dimension) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: 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_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) NavMeshTriangulation obj = NavMesh.CalculateTriangulation(); APILogger.Debug("Merging nearby vertices..."); Vector3[] vertices = Il2CppArrayBase<Vector3>.op_Implicit((Il2CppArrayBase<Vector3>)(object)obj.vertices); int[] indices = Il2CppArrayBase<int>.op_Implicit((Il2CppArrayBase<int>)(object)obj.indices); (v