Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EmotePlaylist v1.0.0
EmotePlaylist.dll
Decompiled 6 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TooManyEmotes; using TooManyEmotes.Patches; using TooManyEmotes.Props; using Unity.Collections; using Unity.Netcode; using UnityEngine; [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("EmotePlaylist")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("EmotePlaylist")] [assembly: AssemblyTitle("EmotePlaylist")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } [CompilerGenerated] [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 EmotePlaylist { internal static class PlaylistNet { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnMessage; } public const string MessageName = "MrGlim.EmotePlaylist"; private static bool _registered; public static void EnsureRegistered() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && !_registered) { CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; object obj = <>O.<0>__OnMessage; if (obj == null) { HandleNamedMessageDelegate val = OnMessage; <>O.<0>__OnMessage = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("MrGlim.EmotePlaylist", (HandleNamedMessageDelegate)obj); _registered = true; } } public unsafe static void BroadcastStart(PlaylistState state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) EnsureRegistered(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return; } FastBufferWriter val = WriteState(1, state); try { if (singleton.IsServer) { singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2); } else { singleton.CustomMessagingManager.SendNamedMessage("MrGlim.EmotePlaylist", 0uL, val, (NetworkDelivery)2); } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } public static void BroadcastStop(ulong ownerPlayerId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) EnsureRegistered(); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null)) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b = 0; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives)); if (singleton.IsServer) { singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2); } else { singleton.CustomMessagingManager.SendNamedMessage("MrGlim.EmotePlaylist", 0uL, val, (NetworkDelivery)2); } ((FastBufferWriter)(ref val)).Dispose(); } } private static FastBufferWriter WriteState(byte op, PlaylistState state) { //IL_0030: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) string text = string.Join("|", state.EmoteNames); int byteCount = Encoding.UTF8.GetByteCount(text); FastBufferWriter result = default(FastBufferWriter); ((FastBufferWriter)(ref result))..ctor(64 + byteCount, (Allocator)2, -1); ((FastBufferWriter)(ref result)).WriteValueSafe<byte>(ref op, default(ForPrimitives)); ((FastBufferWriter)(ref result)).WriteValueSafe<ulong>(ref state.OwnerPlayerId, default(ForPrimitives)); ((FastBufferWriter)(ref result)).WriteValueSafe<double>(ref state.StartServerTime, default(ForPrimitives)); ((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref state.SecondsPerEmote, default(ForPrimitives)); ((FastBufferWriter)(ref result)).WriteValueSafe(text, false); return result; } private unsafe static void OnMessage(ulong sender, FastBufferReader reader) { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); ulong ownerPlayerId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives)); NetworkManager singleton = NetworkManager.Singleton; if (b == 0) { PlaylistRegistry.Clear(ownerPlayerId); if ((Object)(object)singleton != (Object)null && singleton.IsServer) { FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); byte b2 = 0; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b2, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref ownerPlayerId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val, (NetworkDelivery)2); ((FastBufferWriter)(ref val)).Dispose(); } return; } double startServerTime = default(double); ((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref startServerTime, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); num = Mathf.Clamp(num, 60, 300); string[] emoteNames = (string.IsNullOrEmpty(text) ? Array.Empty<string>() : text.Split('|')); PlaylistState state = new PlaylistState { OwnerPlayerId = ownerPlayerId, StartServerTime = startServerTime, SecondsPerEmote = num, EmoteNames = emoteNames }; PlaylistRegistry.Set(state); if (!((Object)(object)singleton != (Object)null) || !singleton.IsServer || sender == 0L) { return; } FastBufferWriter val2 = WriteState(1, state); try { singleton.CustomMessagingManager.SendNamedMessageToAll("MrGlim.EmotePlaylist", val2, (NetworkDelivery)2); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } } internal sealed class PlaylistRunner : MonoBehaviour { private ulong _followingOwner; private int _lastIndex = -1; private bool _running; private string _tip = ""; private void Update() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Enabled == null || !Plugin.Enabled.Value) { return; } PlaylistNet.EnsureRegistered(); PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.isPlayerDead) { StopLocal(_running); return; } if (Input.GetKeyDown(Plugin.PlaylistKey.Value) && !val.isTypingChat && !val.inTerminalMenu) { ToggleOwnPlaylist(val); } if (!_running) { return; } if (!PlaylistRegistry.TryGet(_followingOwner, out PlaylistState state) || state.EmoteNames.Length == 0) { StopLocal(broadcast: false); return; } double num = ServerNow(); int num2 = state.IndexAt(num); if (num2 != _lastIndex) { PlayIndex(state, num2); _lastIndex = num2; } int num3 = state.SecondsPerEmote - (int)((num - state.StartServerTime) % (double)state.SecondsPerEmote); if (num3 <= 0) { num3 = state.SecondsPerEmote; } _tip = $"Playlist {num2 + 1}/{state.EmoteNames.Length} {state.EmoteNames[num2]} {num3}s"; } internal void Join(ulong ownerPlayerId) { if (PlaylistRegistry.TryGet(ownerPlayerId, out PlaylistState state) && state.EmoteNames.Length != 0) { _followingOwner = ownerPlayerId; _running = true; _lastIndex = -1; double serverTime = ServerNow(); int num = state.IndexAt(serverTime); PlayIndex(state, num); _lastIndex = num; Plugin.Log.LogInfo((object)$"Joined playlist owner={ownerPlayerId} index={num} duration={state.SecondsPerEmote}s"); } } private void ToggleOwnPlaylist(PlayerControllerB player) { if (_running && _followingOwner == player.playerClientId) { StopLocal(broadcast: true); return; } List<string> list = BuildPlaylist(); if (list.Count == 0) { Plugin.Log.LogWarning((object)"Playlist empty — assign quick emote slots (wheel remotes) first."); return; } PlaylistState playlistState = new PlaylistState { OwnerPlayerId = player.playerClientId, StartServerTime = ServerNow(), SecondsPerEmote = Plugin.ClampedSeconds(), EmoteNames = list.ToArray() }; PlaylistRegistry.Set(playlistState); PlaylistNet.BroadcastStart(playlistState); _followingOwner = player.playerClientId; _running = true; _lastIndex = -1; Plugin.Log.LogInfo((object)$"Started playlist ({list.Count} emotes, {playlistState.SecondsPerEmote}s each)."); } private void StopLocal(bool broadcast) { if (_running) { ulong followingOwner = _followingOwner; _running = false; _lastIndex = -1; if (broadcast) { PlaylistRegistry.Clear(followingOwner); PlaylistNet.BroadcastStop(followingOwner); Plugin.Log.LogInfo((object)"Stopped playlist."); } } } private static List<string> BuildPlaylist() { List<string> list = new List<string>(); string value = Plugin.PlaylistOverride.Value; if (!string.IsNullOrWhiteSpace(value)) { string[] array = value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { list.Add(text); } } return list; } for (int j = 0; j < 8; j++) { try { UnlockableEmote quickEmote = QuickEmotes.GetQuickEmote(j); if (quickEmote != null && !string.IsNullOrEmpty(quickEmote.emoteName)) { list.Add(quickEmote.emoteName); } } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Quick slot {j} failed: {ex.Message}"); } } return list; } private static void PlayIndex(PlaylistState state, int index) { EmoteControllerPlayer emoteControllerLocal = EmoteControllerPlayer.emoteControllerLocal; if ((Object)(object)emoteControllerLocal == (Object)null || index < 0 || index >= state.EmoteNames.Length) { return; } string text = state.EmoteNames[index]; if (!EmotesManager.allUnlockableEmotesDict.TryGetValue(text, out var value) || value == null) { Plugin.Log.LogWarning((object)("Playlist emote not found: " + text)); return; } try { emoteControllerLocal.TryPerformingEmoteLocal(value, -1, (GrabbablePropObject)null); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to play " + text + ": " + ex.Message)); } } private static double ServerNow() { //IL_0016: 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) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return Time.realtimeSinceStartupAsDouble; } NetworkTime serverTime = singleton.ServerTime; return ((NetworkTime)(ref serverTime)).Time; } } [HarmonyPatch(typeof(SyncWithEmoteControllerManager), "SyncWithEmoteController_performed")] internal static class SyncPlaylistPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(PlayerControllerB __instance) { if (Plugin.Enabled == null || !Plugin.Enabled.Value) { return true; } EmoteController lookingAtSyncableEmoteController = SyncWithEmoteControllerManager.lookingAtSyncableEmoteController; EmoteControllerPlayer val = (EmoteControllerPlayer)(object)((lookingAtSyncableEmoteController is EmoteControllerPlayer) ? lookingAtSyncableEmoteController : null); if (val == null || (Object)(object)val.playerController == (Object)null) { return true; } ulong playerClientId = val.playerController.playerClientId; if (!PlaylistRegistry.TryGet(playerClientId, out PlaylistState _)) { return true; } (((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent<PlaylistRunner>() : null)?.Join(playerClientId); SyncWithEmoteControllerManager.ResetState(); return false; } } internal sealed class PlaylistState { public ulong OwnerPlayerId; public double StartServerTime; public int SecondsPerEmote; public string[] EmoteNames = Array.Empty<string>(); public int IndexAt(double serverTime) { if (EmoteNames.Length == 0 || SecondsPerEmote <= 0) { return 0; } double num = serverTime - StartServerTime; if (num < 0.0) { num = 0.0; } int num2 = (int)(num / (double)SecondsPerEmote) % EmoteNames.Length; if (num2 >= 0) { return num2; } return num2 + EmoteNames.Length; } } internal static class PlaylistRegistry { private static readonly Dictionary<ulong, PlaylistState> ByOwner = new Dictionary<ulong, PlaylistState>(); public static void Set(PlaylistState state) { ByOwner[state.OwnerPlayerId] = state; } public static void Clear(ulong ownerPlayerId) { ByOwner.Remove(ownerPlayerId); } public static bool TryGet(ulong ownerPlayerId, out PlaylistState state) { return ByOwner.TryGetValue(ownerPlayerId, out state); } public static void ClearAll() { ByOwner.Clear(); } } [BepInPlugin("com.benhough.lethal.EmotePlaylist", "EmotePlaylist", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.benhough.lethal.EmotePlaylist"; public const string ModName = "EmotePlaylist"; public const string ModVersion = "1.0.0"; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<KeyCode> PlaylistKey { get; private set; } internal static ConfigEntry<int> SecondsPerEmote { get; private set; } internal static ConfigEntry<string> PlaylistOverride { get; private set; } private void Awake() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable emote playlists."); PlaylistKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "PlaylistKey", (KeyCode)290, "Start/stop a looping playlist of your 8 TooManyEmotes quick slots (the wheel remotes)."); SecondsPerEmote = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SecondsPerEmote", 120, "How long each playlist emote plays (clamped 60–300)."); PlaylistOverride = ((BaseUnityPlugin)this).Config.Bind<string>("General", "PlaylistOverride", "", "Optional comma-separated emote names. Empty = use current quick slots 1–8."); try { new Harmony("com.benhough.lethal.EmotePlaylist").PatchAll(typeof(Plugin).Assembly); ((Component)this).gameObject.AddComponent<PlaylistRunner>(); Log.LogInfo((object)"EmotePlaylist v1.0.0 loaded."); } catch (Exception arg) { Log.LogError((object)$"Failed to start: {arg}"); } } internal static int ClampedSeconds() { return Mathf.Clamp(SecondsPerEmote?.Value ?? 120, 60, 300); } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.benhough.lethal.EmotePlaylist"; public const string PLUGIN_NAME = "EmotePlaylist"; public const string PLUGIN_VERSION = "1.0.0"; } }