Decompiled source of LethalWebsocketEvents v1.0.3

plugins\com.github.luckofthelefty.LethalWebsocketEvents.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Unity.Netcode;
using UnityEngine;
using WebSocketSharp.Server;
using com.github.luckofthelefty.LethalWebsocketEvents.Events;
using com.github.luckofthelefty.LethalWebsocketEvents.Helpers;
using com.github.luckofthelefty.LethalWebsocketEvents.Managers;
using com.github.luckofthelefty.LethalWebsocketEvents.Patches;
using com.github.luckofthelefty.LethalWebsocketEvents.Server;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LuckOfTheLefty")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Streams Lethal Company in-game events (deaths, monsters, items, rounds) over WebSocket for Streamerbot integration.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+636990f9269cff6b2f191ee6269052b009b73676")]
[assembly: AssemblyProduct("LethalWebsocketEvents")]
[assembly: AssemblyTitle("com.github.luckofthelefty.LethalWebsocketEvents")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents
{
	internal static class Logger
	{
		public static ManualLogSource ManualLogSource { get; private set; }

		public static void Initialize(ManualLogSource manualLogSource)
		{
			ManualLogSource = manualLogSource;
		}

		public static void LogDebug(object data)
		{
			Log((LogLevel)32, data);
		}

		public static void LogInfo(object data)
		{
			Log((LogLevel)16, data);
		}

		public static void LogWarning(object data)
		{
			Log((LogLevel)4, data);
		}

		public static void LogError(object data)
		{
			Log((LogLevel)2, data);
		}

		public static void LogFatal(object data)
		{
			Log((LogLevel)1, data);
		}

		public static void Log(LogLevel logLevel, object data)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource manualLogSource = ManualLogSource;
			if (manualLogSource != null)
			{
				manualLogSource.Log(logLevel, data);
			}
		}
	}
	[BepInPlugin("com.github.luckofthelefty.LethalWebsocketEvents", "LethalWebsocketEvents", "1.0.0")]
	internal class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("com.github.luckofthelefty.LethalWebsocketEvents");

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger.Initialize(Logger.CreateLogSource("com.github.luckofthelefty.LethalWebsocketEvents"));
			Logger.LogInfo("LethalWebsocketEvents v1.0.0 loaded!");
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
			_harmony.PatchAll(typeof(PlayerDeathPatch));
			_harmony.PatchAll(typeof(PlayerDamagePatch));
			_harmony.PatchAll(typeof(EmotePatch));
			_harmony.PatchAll(typeof(RoundPatch));
			_harmony.PatchAll(typeof(QuotaPatch));
			_harmony.PatchAll(typeof(EnemyPatch));
			_harmony.PatchAll(typeof(ItemPatch));
			_harmony.PatchAll(typeof(ApparatusPatch));
			_harmony.PatchAll(typeof(LandminePatch));
			_harmony.PatchAll(typeof(TurretPatch));
			_harmony.PatchAll(typeof(TeleporterPatch));
			_harmony.PatchAll(typeof(BrackenPatch));
			_harmony.PatchAll(typeof(JesterPatch));
			_harmony.PatchAll(typeof(GhostGirlPatch));
			_harmony.PatchAll(typeof(CoilheadPatch));
			_harmony.PatchAll(typeof(MaskedPatch));
			_harmony.PatchAll(typeof(NutcrackerPatch));
			_harmony.PatchAll(typeof(ForestGiantPatch));
			_harmony.PatchAll(typeof(SnareFleaPatch));
			_harmony.PatchAll(typeof(SpiderPatch));
			_harmony.PatchAll(typeof(BlobPatch));
			_harmony.PatchAll(typeof(ThumperPatch));
			_harmony.PatchAll(typeof(EyelessDogPatch));
			_harmony.PatchAll(typeof(BaboonHawkPatch));
			_harmony.PatchAll(typeof(ConnectionPatch));
			_harmony.PatchAll(typeof(DisconnectPatch));
			_harmony.PatchAll(typeof(VoteToLeavePatch));
			Task.Run((Action)EventServer.Initialize);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "com.github.luckofthelefty.LethalWebsocketEvents";

		public const string PLUGIN_NAME = "LethalWebsocketEvents";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents.Server
{
	public class EventBehavior : WebSocketBehavior
	{
		protected override void OnOpen()
		{
			GameEvent gameEvent = GameEvent.Create("connected", new Dictionary<string, object>
			{
				{ "modVersion", "1.0.0" },
				{ "modName", "LethalWebsocketEvents" }
			});
			((WebSocketBehavior)this).Send(JsonConvert.SerializeObject((object)gameEvent));
		}
	}
	internal static class EventServer
	{
		private static WebSocketServer _webSocketServer;

		private static readonly object _broadcastLock = new object();

		public static int WebSocketPort => ConfigManager.Server_WebSocketPort.Value;

		public static bool IsRunning { get; private set; }

		public static void Initialize()
		{
			Application.quitting += delegate
			{
				Stop();
			};
			if (ConfigManager.Server_AutoStart.Value)
			{
				Start();
			}
		}

		public static void Start()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			if (IsRunning)
			{
				Logger.LogWarning("Event server is already running!");
				return;
			}
			try
			{
				IsRunning = true;
				_webSocketServer = new WebSocketServer($"ws://{IPAddress.Any}:{WebSocketPort}");
				_webSocketServer.AddWebSocketService<EventBehavior>("/events");
				_webSocketServer.Start();
				if (!_webSocketServer.IsListening)
				{
					Logger.LogError("Failed to start WebSocket server. The port might already be in use.");
					Stop();
				}
				else
				{
					Logger.LogInfo($"WebSocket event server started on ws://localhost:{WebSocketPort}/events");
				}
			}
			catch (SocketException ex)
			{
				Logger.LogError("Failed to start WebSocket server. " + ex.Message);
				Stop();
			}
			catch (Exception arg)
			{
				Logger.LogError($"Failed to start WebSocket server. {arg}");
				Stop();
			}
		}

		public static void Stop()
		{
			if (IsRunning)
			{
				IsRunning = false;
				WebSocketServer webSocketServer = _webSocketServer;
				if (webSocketServer != null)
				{
					webSocketServer.Stop();
				}
				_webSocketServer = null;
				Logger.LogInfo("Event server stopped.");
			}
		}

		public static void SendEvent(GameEvent gameEvent)
		{
			if (_webSocketServer == null || !_webSocketServer.IsListening)
			{
				return;
			}
			lock (_broadcastLock)
			{
				try
				{
					string text = JsonConvert.SerializeObject((object)gameEvent);
					if (ConfigManager.ExtendedLogging.Value)
					{
						Logger.LogInfo("Broadcasting event: " + text);
					}
					foreach (string path in _webSocketServer.WebSocketServices.Paths)
					{
						WebSocketServiceHost val = _webSocketServer.WebSocketServices[path];
						val.Sessions.Broadcast(text);
					}
				}
				catch (Exception arg)
				{
					Logger.LogError($"Failed to broadcast event. {arg}");
				}
			}
		}

		public static void SendEvent(string eventName, Dictionary<string, object> data = null)
		{
			SendEvent(GameEvent.Create(eventName, data));
		}
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents.Patches
{
	[HarmonyPatch(typeof(EnemyAI))]
	internal static class EnemyPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void StartPatch(EnemyAI __instance)
		{
			if (!((Object)(object)__instance.enemyType == (Object)null))
			{
				string value = __instance.enemyType.enemyName ?? ((object)__instance).GetType().Name;
				bool isOutside = __instance.isOutside;
				EventServer.SendEvent("enemy_spawned", new Dictionary<string, object>
				{
					{ "enemyType", value },
					{
						"location",
						isOutside ? "outdoor" : "indoor"
					}
				});
			}
		}

		[HarmonyPatch("KillEnemyClientRpc")]
		[HarmonyPostfix]
		private static void KillEnemyClientRpcPatch(EnemyAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"enemy_killed_{((Object)__instance).GetInstanceID()}") && !((Object)(object)__instance.enemyType == (Object)null))
			{
				string value = __instance.enemyType.enemyName ?? ((object)__instance).GetType().Name;
				EventServer.SendEvent("enemy_killed", new Dictionary<string, object> { { "enemyType", value } });
			}
		}

		[HarmonyPatch("SwitchToBehaviourClientRpc")]
		[HarmonyPostfix]
		private static void SwitchToBehaviourClientRpcPatch(EnemyAI __instance, int stateIndex)
		{
			if (NetworkUtils.ShouldProcess($"enemy_state_{((Object)__instance).GetInstanceID()}_{stateIndex}") && !((Object)(object)__instance.enemyType == (Object)null))
			{
				string value = __instance.enemyType.enemyName ?? ((object)__instance).GetType().Name;
				EventServer.SendEvent("enemy_state_changed", new Dictionary<string, object>
				{
					{ "enemyType", value },
					{ "state", stateIndex }
				});
			}
		}

		[HarmonyPatch("HitEnemyClientRpc")]
		[HarmonyPostfix]
		private static void HitEnemyClientRpcPatch(EnemyAI __instance, int force, int playerWhoHit, bool playHitSFX, int hitID)
		{
			if (NetworkUtils.ShouldProcess($"enemy_hit_{((Object)__instance).GetInstanceID()}_{hitID}") && !((Object)(object)__instance.enemyType == (Object)null))
			{
				string value = __instance.enemyType.enemyName ?? ((object)__instance).GetType().Name;
				string playerName = PlayerUtils.GetPlayerName(playerWhoHit);
				EventServer.SendEvent("enemy_hit", new Dictionary<string, object>
				{
					{ "enemyType", value },
					{ "player", playerName },
					{ "force", force }
				});
			}
		}
	}
	[HarmonyPatch(typeof(Landmine))]
	internal static class LandminePatch
	{
		[HarmonyPatch("ExplodeMineClientRpc")]
		[HarmonyPostfix]
		private static void ExplodeMineClientRpcPatch(Landmine __instance)
		{
			if (NetworkUtils.ShouldProcess($"landmine_{((Object)__instance).GetInstanceID()}"))
			{
				EventServer.SendEvent("landmine_exploded", new Dictionary<string, object>());
			}
		}
	}
	[HarmonyPatch(typeof(Turret))]
	internal static class TurretPatch
	{
		[HarmonyPatch("SetToModeClientRpc")]
		[HarmonyPostfix]
		private static void SetToModeClientRpcPatch(Turret __instance, int mode)
		{
			if (NetworkUtils.ShouldProcess($"turret_{((Object)__instance).GetInstanceID()}_{mode}"))
			{
				string[] array = new string[4] { "Detection", "Charging", "Firing", "Berserk" };
				string value = ((mode >= 0 && mode < array.Length) ? array[mode] : "Unknown");
				EventServer.SendEvent("turret_mode_changed", new Dictionary<string, object>
				{
					{ "mode", value },
					{ "modeIndex", mode }
				});
			}
		}
	}
	[HarmonyPatch(typeof(ShipTeleporter))]
	internal static class TeleporterPatch
	{
		[HarmonyPatch("PressTeleportButtonClientRpc")]
		[HarmonyPostfix]
		private static void PressTeleportButtonClientRpcPatch(ShipTeleporter __instance)
		{
			if (NetworkUtils.ShouldProcess($"teleporter_{((Object)__instance).GetInstanceID()}"))
			{
				EventServer.SendEvent("teleporter_used", new Dictionary<string, object> { { "isInverse", __instance.isInverseTeleporter } });
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class ItemPatch
	{
		[HarmonyPatch("GrabObjectClientRpc")]
		[HarmonyPostfix]
		private static void GrabObjectClientRpcPatch(PlayerControllerB __instance, NetworkObjectReference grabbedObject)
		{
			if (NetworkUtils.ShouldProcess($"item_grab_{((Object)__instance).GetInstanceID()}") && PlayerUtils.ShouldTrackPlayer(__instance))
			{
				string playerName = PlayerUtils.GetPlayerName(__instance);
				string value = "Unknown";
				int num = 0;
				NetworkObject val = default(NetworkObject);
				GrabbableObject val2 = default(GrabbableObject);
				if (((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) && ((Component)val).TryGetComponent<GrabbableObject>(ref val2))
				{
					value = val2.itemProperties?.itemName ?? "Unknown";
					num = val2.scrapValue;
				}
				EventServer.SendEvent("item_grabbed", new Dictionary<string, object>
				{
					{ "player", playerName },
					{ "item", value },
					{ "scrapValue", num }
				});
			}
		}

		[HarmonyPatch("ThrowObjectClientRpc")]
		[HarmonyPostfix]
		private static void ThrowObjectClientRpcPatch(PlayerControllerB __instance, bool droppedInElevator, bool droppedInShipRoom)
		{
			if (NetworkUtils.ShouldProcess($"item_drop_{((Object)__instance).GetInstanceID()}") && PlayerUtils.ShouldTrackPlayer(__instance))
			{
				string playerName = PlayerUtils.GetPlayerName(__instance);
				EventServer.SendEvent("item_dropped", new Dictionary<string, object>
				{
					{ "player", playerName },
					{
						"inShip",
						droppedInElevator || droppedInShipRoom
					}
				});
			}
		}
	}
	[HarmonyPatch(typeof(LungProp))]
	internal static class ApparatusPatch
	{
		[HarmonyPatch("DisconnectFromMachinery")]
		[HarmonyPostfix]
		private static void DisconnectFromMachineryPatch(LungProp __instance)
		{
			string value = "Unknown";
			if (((GrabbableObject)__instance).isHeld && (Object)(object)((GrabbableObject)__instance).playerHeldBy != (Object)null)
			{
				if (!PlayerUtils.ShouldTrackPlayer(((GrabbableObject)__instance).playerHeldBy))
				{
					return;
				}
				value = PlayerUtils.GetPlayerName(((GrabbableObject)__instance).playerHeldBy);
			}
			EventServer.SendEvent("apparatus_pulled", new Dictionary<string, object> { { "player", value } });
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class ConnectionPatch
	{
		[HarmonyPatch("SendNewPlayerValuesClientRpc")]
		[HarmonyPostfix]
		private static void SendNewPlayerValuesClientRpcPatch(PlayerControllerB __instance)
		{
			if (NetworkUtils.ShouldProcess($"player_joined_{((Object)__instance).GetInstanceID()}"))
			{
				string playerName = PlayerUtils.GetPlayerName(__instance);
				int connectedPlayerCount = PlayerUtils.GetConnectedPlayerCount();
				EventServer.SendEvent("player_joined", new Dictionary<string, object>
				{
					{ "player", playerName },
					{ "playerCount", connectedPlayerCount }
				});
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class DisconnectPatch
	{
		[HarmonyPatch("OnPlayerDC")]
		[HarmonyPostfix]
		private static void OnPlayerDCPatch(int playerObjectNumber)
		{
			string value = "Unknown";
			if (StartOfRound.Instance?.allPlayerScripts != null && playerObjectNumber >= 0 && playerObjectNumber < StartOfRound.Instance.allPlayerScripts.Length)
			{
				value = PlayerUtils.GetPlayerName(StartOfRound.Instance.allPlayerScripts[playerObjectNumber]);
			}
			int connectedPlayerCount = PlayerUtils.GetConnectedPlayerCount();
			EventServer.SendEvent("player_left", new Dictionary<string, object>
			{
				{ "player", value },
				{ "playerCount", connectedPlayerCount }
			});
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class EmotePatch
	{
		[HarmonyPatch("PerformEmote")]
		[HarmonyPostfix]
		private static void PerformEmotePatch(PlayerControllerB __instance, int emoteID)
		{
			if (PlayerUtils.ShouldTrackPlayer(__instance))
			{
				string playerName = PlayerUtils.GetPlayerName(__instance);
				EventServer.SendEvent("player_emote", new Dictionary<string, object>
				{
					{ "player", playerName },
					{ "emoteId", emoteID }
				});
			}
		}
	}
	[HarmonyPatch(typeof(TimeOfDay))]
	internal static class VoteToLeavePatch
	{
		[HarmonyPatch("VoteShipToLeaveEarly")]
		[HarmonyPostfix]
		private static void VoteShipToLeaveEarlyPatch()
		{
			EventServer.SendEvent("vote_to_leave", new Dictionary<string, object>());
		}
	}
	[HarmonyPatch(typeof(FlowermanAI))]
	internal static class BrackenPatch
	{
		[HarmonyPatch("KillPlayerAnimationClientRpc")]
		[HarmonyPostfix]
		private static void KillPlayerAnimationClientRpcPatch(FlowermanAI __instance, int playerObjectId)
		{
			if (NetworkUtils.ShouldProcess($"bracken_grab_{playerObjectId}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerObjectId);
				EventServer.SendEvent("bracken_grab", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(JesterAI))]
	internal static class JesterPatch
	{
		[HarmonyPatch("KillPlayerClientRpc")]
		[HarmonyPostfix]
		private static void KillPlayerClientRpcPatch(JesterAI __instance, int playerId)
		{
			if (NetworkUtils.ShouldProcess($"jester_kill_{playerId}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerId);
				EventServer.SendEvent("jester_kill", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(DressGirlAI))]
	internal static class GhostGirlPatch
	{
		[HarmonyPatch("ChooseNewHauntingPlayerClientRpc")]
		[HarmonyPostfix]
		private static void ChooseNewHauntingPlayerClientRpcPatch(DressGirlAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"ghost_haunt_{((Object)__instance).GetInstanceID()}"))
			{
				string value = "Unknown";
				if ((Object)(object)__instance.hauntingPlayer != (Object)null)
				{
					value = PlayerUtils.GetPlayerName(__instance.hauntingPlayer);
				}
				EventServer.SendEvent("ghost_girl_haunt", new Dictionary<string, object> { { "player", value } });
			}
		}
	}
	[HarmonyPatch(typeof(SpringManAI))]
	internal static class CoilheadPatch
	{
		[HarmonyPatch("SetAnimationGoClientRpc")]
		[HarmonyPostfix]
		private static void SetAnimationGoClientRpcPatch(SpringManAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"coilhead_go_{((Object)__instance).GetInstanceID()}"))
			{
				EventServer.SendEvent("coilhead_moving", new Dictionary<string, object>());
			}
		}

		[HarmonyPatch("SetAnimationStopClientRpc")]
		[HarmonyPostfix]
		private static void SetAnimationStopClientRpcPatch(SpringManAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"coilhead_stop_{((Object)__instance).GetInstanceID()}"))
			{
				EventServer.SendEvent("coilhead_stopped", new Dictionary<string, object>());
			}
		}
	}
	[HarmonyPatch(typeof(MaskedPlayerEnemy))]
	internal static class MaskedPatch
	{
		[HarmonyPatch("CreateMimicClientRpc")]
		[HarmonyPostfix]
		private static void CreateMimicClientRpcPatch(MaskedPlayerEnemy __instance)
		{
			if (NetworkUtils.ShouldProcess($"masked_mimic_{((Object)__instance).GetInstanceID()}"))
			{
				string value = (((Object)(object)__instance.mimickingPlayer != (Object)null) ? PlayerUtils.GetPlayerName(__instance.mimickingPlayer) : "Unknown");
				EventServer.SendEvent("masked_mimic", new Dictionary<string, object> { { "mimicking", value } });
			}
		}
	}
	[HarmonyPatch(typeof(NutcrackerEnemyAI))]
	internal static class NutcrackerPatch
	{
		[HarmonyPatch("FireGunClientRpc")]
		[HarmonyPostfix]
		private static void FireGunClientRpcPatch(NutcrackerEnemyAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"nutcracker_shot_{((Object)__instance).GetInstanceID()}"))
			{
				EventServer.SendEvent("nutcracker_shot", new Dictionary<string, object>());
			}
		}
	}
	[HarmonyPatch(typeof(ForestGiantAI))]
	internal static class ForestGiantPatch
	{
		[HarmonyPatch("GrabPlayerClientRpc")]
		[HarmonyPostfix]
		private static void GrabPlayerClientRpcPatch(ForestGiantAI __instance, int playerId)
		{
			if (NetworkUtils.ShouldProcess($"giant_grab_{playerId}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerId);
				EventServer.SendEvent("giant_grab", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(CentipedeAI))]
	internal static class SnareFleaPatch
	{
		[HarmonyPatch("ClingToPlayerClientRpc")]
		[HarmonyPostfix]
		private static void ClingToPlayerClientRpcPatch(CentipedeAI __instance)
		{
			if (NetworkUtils.ShouldProcess($"snare_flea_{((Object)__instance).GetInstanceID()}"))
			{
				string value = "Unknown";
				if ((Object)(object)__instance.clingingToPlayer != (Object)null)
				{
					value = PlayerUtils.GetPlayerName(__instance.clingingToPlayer);
				}
				EventServer.SendEvent("snare_flea_cling", new Dictionary<string, object> { { "player", value } });
			}
		}
	}
	[HarmonyPatch(typeof(SandSpiderAI))]
	internal static class SpiderPatch
	{
		[HarmonyPatch("PlayerTripWebClientRpc")]
		[HarmonyPostfix]
		private static void PlayerTripWebClientRpcPatch(SandSpiderAI __instance, int playerNum)
		{
			if (NetworkUtils.ShouldProcess($"spider_web_{((Object)__instance).GetInstanceID()}_{playerNum}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerNum);
				EventServer.SendEvent("spider_web_trip", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(BlobAI))]
	internal static class BlobPatch
	{
		[HarmonyPatch("SlimeKillPlayerEffectClientRpc")]
		[HarmonyPostfix]
		private static void SlimeKillPlayerEffectClientRpcPatch(BlobAI __instance, int playerKilled)
		{
			if (NetworkUtils.ShouldProcess($"blob_kill_{playerKilled}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerKilled);
				EventServer.SendEvent("blob_kill", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(CrawlerAI))]
	internal static class ThumperPatch
	{
		[HarmonyPatch("HitPlayerClientRpc")]
		[HarmonyPostfix]
		private static void HitPlayerClientRpcPatch(CrawlerAI __instance, int playerId)
		{
			if (NetworkUtils.ShouldProcess($"thumper_hit_{playerId}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerId);
				EventServer.SendEvent("thumper_hit", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(MouthDogAI))]
	internal static class EyelessDogPatch
	{
		[HarmonyPatch("KillPlayerClientRpc")]
		[HarmonyPostfix]
		private static void KillPlayerClientRpcPatch(MouthDogAI __instance, int playerId)
		{
			if (NetworkUtils.ShouldProcess($"dog_kill_{playerId}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerId);
				EventServer.SendEvent("eyeless_dog_kill", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(BaboonBirdAI))]
	internal static class BaboonHawkPatch
	{
		[HarmonyPatch("StabPlayerDeathAnimClientRpc")]
		[HarmonyPostfix]
		private static void StabPlayerDeathAnimClientRpcPatch(BaboonBirdAI __instance, int playerObject)
		{
			if (NetworkUtils.ShouldProcess($"baboon_stab_{playerObject}"))
			{
				string playerName = PlayerUtils.GetPlayerName(playerObject);
				EventServer.SendEvent("baboon_hawk_stab", new Dictionary<string, object> { { "player", playerName } });
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerDamagePatch
	{
		[HarmonyPatch("DamagePlayerClientRpc")]
		[HarmonyPostfix]
		private static void DamagePlayerClientRpcPatch(PlayerControllerB __instance, int damageNumber)
		{
			if (!NetworkUtils.ShouldProcess($"damage_{((Object)__instance).GetInstanceID()}_{damageNumber}") || !PlayerUtils.ShouldTrackPlayer(__instance))
			{
				return;
			}
			string playerName = PlayerUtils.GetPlayerName(__instance);
			if (damageNumber < 0)
			{
				EventServer.SendEvent("player_healed", new Dictionary<string, object>
				{
					{ "player", playerName },
					{
						"amount",
						-damageNumber
					},
					{ "health", __instance.health }
				});
				return;
			}
			string text = EnemyUtils.FindAttackingEnemy(__instance);
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				{ "player", playerName },
				{ "damage", damageNumber },
				{ "health", __instance.health },
				{
					"critical",
					__instance.health <= 0
				}
			};
			if (text != null)
			{
				dictionary["enemy"] = text;
			}
			EventServer.SendEvent("player_damage", dictionary);
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerDeathPatch
	{
		[HarmonyPatch("KillPlayerClientRpc")]
		[HarmonyPostfix]
		private static void KillPlayerClientRpcPatch(PlayerControllerB __instance, int playerId, int causeOfDeath)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkUtils.ShouldProcess($"death_{playerId}"))
			{
				return;
			}
			PlayerControllerB playerScript = ((StartOfRound.Instance?.allPlayerScripts != null && playerId >= 0 && playerId < StartOfRound.Instance.allPlayerScripts.Length) ? StartOfRound.Instance.allPlayerScripts[playerId] : __instance);
			if (PlayerUtils.ShouldTrackPlayer(playerScript))
			{
				string playerName = PlayerUtils.GetPlayerName(playerScript);
				CauseOfDeath val = (CauseOfDeath)causeOfDeath;
				string value = ((object)(CauseOfDeath)(ref val)).ToString();
				string text = EnemyUtils.FindAttackingEnemy(playerScript);
				Dictionary<string, object> dictionary = new Dictionary<string, object>
				{
					{ "player", playerName },
					{ "causeOfDeath", value },
					{ "playerId", playerId }
				};
				if (text != null)
				{
					dictionary["enemy"] = text;
				}
				EventServer.SendEvent("player_death", dictionary);
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class RoundPatch
	{
		private static DateTime _lastDayChangedTime = DateTime.MinValue;

		private static string _lastDayChangedKey = "";

		private static readonly TimeSpan EventDebounce = TimeSpan.FromSeconds(2.0);

		private static DateTime _lastShipLandedTime = DateTime.MinValue;

		[HarmonyPatch("StartGame")]
		[HarmonyPostfix]
		private static void StartGamePatch()
		{
			string value = StartOfRound.Instance?.currentLevel?.PlanetName ?? "Unknown";
			StartOfRound instance = StartOfRound.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				SelectableLevel currentLevel = instance.currentLevel;
				obj = ((currentLevel != null) ? ((object)(LevelWeatherType)(ref currentLevel.currentWeather)).ToString() : null);
			}
			if (obj == null)
			{
				obj = "None";
			}
			string value2 = (string)obj;
			EventServer.SendEvent("round_start", new Dictionary<string, object>
			{
				{ "moon", value },
				{ "weather", value2 }
			});
		}

		[HarmonyPatch("EndOfGame")]
		[HarmonyPrefix]
		private static void EndOfGamePatch()
		{
			EventServer.SendEvent("round_end", new Dictionary<string, object>());
		}

		[HarmonyPatch("ShipLeave")]
		[HarmonyPostfix]
		private static void ShipLeavePatch()
		{
			EventServer.SendEvent("ship_leaving", new Dictionary<string, object>());
		}

		[HarmonyPatch("ReviveDeadPlayers")]
		[HarmonyPostfix]
		private static void ReviveDeadPlayersPatch()
		{
			EventServer.SendEvent("players_revived", new Dictionary<string, object>());
		}

		[HarmonyPatch("SetMapScreenInfoToCurrentLevel")]
		[HarmonyPostfix]
		private static void SetMapScreenInfoToCurrentLevelPatch()
		{
			string text = StartOfRound.Instance?.currentLevel?.PlanetName ?? "Unknown";
			StartOfRound instance = StartOfRound.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				SelectableLevel currentLevel = instance.currentLevel;
				obj = ((currentLevel != null) ? ((object)(LevelWeatherType)(ref currentLevel.currentWeather)).ToString() : null);
			}
			if (obj == null)
			{
				obj = "None";
			}
			string text2 = (string)obj;
			string text3 = text + "|" + text2;
			DateTime utcNow = DateTime.UtcNow;
			if (!(utcNow - _lastDayChangedTime < EventDebounce) || !(_lastDayChangedKey == text3))
			{
				_lastDayChangedTime = utcNow;
				_lastDayChangedKey = text3;
				EventServer.SendEvent("day_changed", new Dictionary<string, object>
				{
					{ "moon", text },
					{ "weather", text2 }
				});
			}
		}

		[HarmonyPatch("ChangeLevelClientRpc")]
		[HarmonyPostfix]
		private static void ChangeLevelClientRpcPatch(StartOfRound __instance)
		{
			if (NetworkUtils.ShouldProcess("moon_changed"))
			{
				string value = StartOfRound.Instance?.currentLevel?.PlanetName ?? "Unknown";
				StartOfRound instance = StartOfRound.Instance;
				object obj;
				if (instance == null)
				{
					obj = null;
				}
				else
				{
					SelectableLevel currentLevel = instance.currentLevel;
					obj = ((currentLevel != null) ? ((object)(LevelWeatherType)(ref currentLevel.currentWeather)).ToString() : null);
				}
				if (obj == null)
				{
					obj = "None";
				}
				string value2 = (string)obj;
				EventServer.SendEvent("moon_changed", new Dictionary<string, object>
				{
					{ "moon", value },
					{ "weather", value2 }
				});
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void OpeningDoorsSequencePatch()
		{
			DateTime utcNow = DateTime.UtcNow;
			if (!(utcNow - _lastShipLandedTime < EventDebounce))
			{
				_lastShipLandedTime = utcNow;
				string value = StartOfRound.Instance?.currentLevel?.PlanetName ?? "Unknown";
				EventServer.SendEvent("ship_landed", new Dictionary<string, object> { { "moon", value } });
			}
		}
	}
	[HarmonyPatch(typeof(TimeOfDay))]
	internal static class QuotaPatch
	{
		[HarmonyPatch("SyncNewProfitQuotaClientRpc")]
		[HarmonyPostfix]
		private static void SyncNewProfitQuotaClientRpcPatch(TimeOfDay __instance)
		{
			if (NetworkUtils.ShouldProcess("quota_fulfilled"))
			{
				int num = TimeOfDay.Instance?.profitQuota ?? 0;
				int num2 = (TimeOfDay.Instance?.timesFulfilledQuota ?? 0) + 1;
				EventServer.SendEvent("quota_fulfilled", new Dictionary<string, object>
				{
					{ "newQuota", num },
					{ "quotaIndex", num2 }
				});
			}
		}
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents.Managers
{
	internal static class ConfigManager
	{
		public static ConfigFile ConfigFile { get; private set; }

		public static ConfigEntry<bool> ExtendedLogging { get; private set; }

		public static ConfigEntry<bool> Server_AutoStart { get; private set; }

		public static ConfigEntry<int> Server_WebSocketPort { get; private set; }

		public static ConfigEntry<bool> Filter_LocalPlayerOnly { get; private set; }

		public static ConfigEntry<string> Filter_PlayerName { get; private set; }

		public static void Initialize(ConfigFile configFile)
		{
			ConfigFile = configFile;
			ExtendedLogging = configFile.Bind<bool>("General", "ExtendedLogging", false, "Enable extended logging for debugging.");
			Server_AutoStart = configFile.Bind<bool>("Server", "AutoStart", true, "If enabled, the WebSocket server will automatically start when the game launches.");
			Server_WebSocketPort = configFile.Bind<int>("Server", "WebSocketPort", 8765, "The WebSocket port for the event server.");
			Filter_LocalPlayerOnly = configFile.Bind<bool>("Filter", "LocalPlayerOnly", false, "If enabled, player-specific events (death, damage, items) only fire for the local player. Global events (enemy spawns, round start, etc.) always fire regardless of this setting.");
			Filter_PlayerName = configFile.Bind<string>("Filter", "PlayerName", "", "If set, player-specific events only fire for this player name. Leave empty to use LocalPlayerOnly logic instead. Useful if you want to track a specific player who isn't running the mod.");
		}
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents.Helpers
{
	internal static class EnemyUtils
	{
		public static string FindAttackingEnemy(PlayerControllerB playerScript)
		{
			if ((Object)(object)playerScript == (Object)null)
			{
				return null;
			}
			if ((Object)(object)playerScript.inAnimationWithEnemy != (Object)null)
			{
				return GetEnemyName(playerScript.inAnimationWithEnemy);
			}
			EnemyAI[] array = Object.FindObjectsOfType<EnemyAI>();
			if (array == null)
			{
				return null;
			}
			EnemyAI[] array2 = array;
			foreach (EnemyAI val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.enemyType == (Object)null))
				{
					if ((Object)(object)val.inSpecialAnimationWithPlayer == (Object)(object)playerScript)
					{
						return GetEnemyName(val);
					}
					if ((Object)(object)val.targetPlayer == (Object)(object)playerScript && val.movingTowardsTargetPlayer)
					{
						return GetEnemyName(val);
					}
				}
			}
			return null;
		}

		private static string GetEnemyName(EnemyAI enemy)
		{
			if ((Object)(object)enemy?.enemyType == (Object)null)
			{
				return null;
			}
			return enemy.enemyType.enemyName ?? ((object)enemy).GetType().Name;
		}
	}
	internal static class NetworkUtils
	{
		private static readonly Dictionary<string, int> _lastProcessedFrame = new Dictionary<string, int>();

		public static bool IsConnected
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return false;
				}
				return singleton.IsConnectedClient;
			}
		}

		public static bool IsServer
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return false;
				}
				return singleton.IsServer;
			}
		}

		public static ulong GetLocalClientId()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton == null)
			{
				return 0uL;
			}
			return singleton.LocalClientId;
		}

		public static bool IsLocalClientId(ulong clientId)
		{
			return clientId == GetLocalClientId();
		}

		public static bool ShouldProcess(string eventKey)
		{
			int frameCount = Time.frameCount;
			if (_lastProcessedFrame.TryGetValue(eventKey, out var value) && value == frameCount)
			{
				return false;
			}
			_lastProcessedFrame[eventKey] = frameCount;
			return true;
		}
	}
	internal static class PlayerUtils
	{
		public static PlayerControllerB GetLocalPlayerScript()
		{
			if ((Object)(object)GameNetworkManager.Instance == (Object)null)
			{
				return null;
			}
			return GameNetworkManager.Instance.localPlayerController;
		}

		public static bool IsLocalPlayer(PlayerControllerB playerScript)
		{
			return (Object)(object)playerScript == (Object)(object)GetLocalPlayerScript();
		}

		public static bool IsLocalPlayerSpawned()
		{
			return (Object)(object)GetLocalPlayerScript() != (Object)null;
		}

		public static PlayerControllerB GetPlayerScriptByClientId(ulong clientId)
		{
			if ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				return null;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (val.actualClientId == clientId)
				{
					return val;
				}
			}
			return null;
		}

		public static int GetConnectedPlayerCount()
		{
			if ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				return 1;
			}
			return StartOfRound.Instance.connectedPlayersAmount + 1;
		}

		public static string GetPlayerName(PlayerControllerB playerScript)
		{
			if ((Object)(object)playerScript == (Object)null)
			{
				return "Unknown";
			}
			return playerScript.playerUsername ?? "Unknown";
		}

		public static string GetPlayerName(int playerObjectIndex)
		{
			if (StartOfRound.Instance?.allPlayerScripts == null || playerObjectIndex < 0 || playerObjectIndex >= StartOfRound.Instance.allPlayerScripts.Length)
			{
				return "Unknown";
			}
			return GetPlayerName(StartOfRound.Instance.allPlayerScripts[playerObjectIndex]);
		}

		public static bool ShouldTrackPlayer(PlayerControllerB playerScript)
		{
			string text = ConfigManager.Filter_PlayerName?.Value;
			if (!string.IsNullOrEmpty(text))
			{
				string playerName = GetPlayerName(playerScript);
				return string.Equals(playerName, text, StringComparison.OrdinalIgnoreCase);
			}
			ConfigEntry<bool> filter_LocalPlayerOnly = ConfigManager.Filter_LocalPlayerOnly;
			if (filter_LocalPlayerOnly != null && filter_LocalPlayerOnly.Value)
			{
				return IsLocalPlayer(playerScript);
			}
			return true;
		}

		public static bool ShouldTrackPlayer(string playerName)
		{
			string text = ConfigManager.Filter_PlayerName?.Value;
			if (!string.IsNullOrEmpty(text))
			{
				return string.Equals(playerName, text, StringComparison.OrdinalIgnoreCase);
			}
			ConfigEntry<bool> filter_LocalPlayerOnly = ConfigManager.Filter_LocalPlayerOnly;
			if (filter_LocalPlayerOnly != null && filter_LocalPlayerOnly.Value)
			{
				PlayerControllerB localPlayerScript = GetLocalPlayerScript();
				if ((Object)(object)localPlayerScript != (Object)null)
				{
					return string.Equals(GetPlayerName(localPlayerScript), playerName, StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}
			return true;
		}

		public static bool ShouldTrackPlayer(int playerId)
		{
			if (StartOfRound.Instance?.allPlayerScripts == null || playerId < 0 || playerId >= StartOfRound.Instance.allPlayerScripts.Length)
			{
				return true;
			}
			return ShouldTrackPlayer(StartOfRound.Instance.allPlayerScripts[playerId]);
		}
	}
}
namespace com.github.luckofthelefty.LethalWebsocketEvents.Events
{
	internal class GameEvent
	{
		[JsonProperty("event")]
		public string Event { get; set; }

		[JsonProperty("timestamp")]
		public string Timestamp { get; set; }

		[JsonProperty("data")]
		public Dictionary<string, object> Data { get; set; }

		public GameEvent(string eventName, Dictionary<string, object> data = null)
		{
			Event = eventName;
			Timestamp = DateTime.UtcNow.ToString("o");
			Data = data ?? new Dictionary<string, object>();
		}

		public static GameEvent Create(string eventName, Dictionary<string, object> data = null)
		{
			return new GameEvent(eventName, data);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}