using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using com.github.zehsteam.TwitchChatAPI.Dependencies;
using com.github.zehsteam.TwitchChatAPI.Enums;
using com.github.zehsteam.TwitchChatAPI.Helpers;
using com.github.zehsteam.TwitchChatAPI.MonoBehaviours;
using com.github.zehsteam.TwitchChatAPI.Objects;
using com.github.zehsteam.TwitchChatAPI.Patches;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.zehsteam.TwitchChatAPI")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+5b3ef0b580c03fd46b25c5fbcea30e90ea6d03bb")]
[assembly: AssemblyProduct("TwitchChatAPI")]
[assembly: AssemblyTitle("com.github.zehsteam.TwitchChatAPI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.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.zehsteam.TwitchChatAPI
{
public static class API
{
public static ConnectionState ConnectionState => TwitchChat.ConnectionState;
public static event Action OnConnect;
public static event Action OnDisconnect;
public static event Action<TwitchMessage> OnMessage;
public static event Action<TwitchCheerEvent> OnCheer;
public static event Action<TwitchSubEvent> OnSub;
public static event Action<TwitchRaidEvent> OnRaid;
public static event Action<TwitchRoomState> OnRoomStateUpdate;
internal static void InvokeOnConnect()
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnConnect?.Invoke();
});
}
internal static void InvokeOnDisconnect()
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnDisconnect?.Invoke();
});
}
internal static void InvokeOnMessage(TwitchMessage message)
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnMessage?.Invoke(message);
});
}
internal static void InvokeOnSub(TwitchSubEvent subEvent)
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnSub?.Invoke(subEvent);
});
}
internal static void InvokeOnCheer(TwitchCheerEvent cheerEvent)
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnCheer?.Invoke(cheerEvent);
});
}
internal static void InvokeOnRaid(TwitchRaidEvent raidEvent)
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnRaid?.Invoke(raidEvent);
});
}
internal static void InvokeOnRoomStateUpdate(TwitchRoomState roomState)
{
MainThreadDispatcher.Enqueue(delegate
{
API.OnRoomStateUpdate?.Invoke(roomState);
});
}
}
internal class ConfigManager
{
public ConfigEntry<bool> ExtendedLogging { get; private set; }
public ConfigEntry<bool> TwitchChat_Enabled { get; private set; }
public ConfigEntry<string> TwitchChat_Channel { get; private set; }
public ConfigManager()
{
BindConfigs();
ConfigHelper.ClearUnusedEntries();
}
private void BindConfigs()
{
ConfigHelper.SkipAutoGen();
ExtendedLogging = ConfigHelper.Bind("General", "ExtendedLogging", defaultValue: false, "Enable extended logging.");
TwitchChat_Enabled = ConfigHelper.Bind("Twitch Chat", "Enabled", defaultValue: true, "Enable/Disable the connection to Twitch chat.");
TwitchChat_Channel = ConfigHelper.Bind("Twitch Chat", "Channel", "", "Your Twitch channel username.");
ConfigHelper.AddButton("Twitch Chat", "Refresh Connection", "Refresh the connection to Twitch chat.", "Refresh", TwitchChat_Refresh_Clicked);
TwitchChat_Enabled.SettingChanged += delegate
{
TwitchChat_Enabled_SettingChanged();
};
TwitchChat_Channel.SettingChanged += delegate
{
TwitchChat_Channel_SettingChanged();
};
}
private void TwitchChat_Enabled_SettingChanged()
{
if (TwitchChat_Enabled.Value)
{
TwitchChat.Connect();
}
else
{
TwitchChat.Disconnect();
}
}
private void TwitchChat_Channel_SettingChanged()
{
if (TwitchChat_Enabled.Value)
{
TwitchChat.Connect();
}
}
private void TwitchChat_Refresh_Clicked()
{
if (TwitchChat_Enabled.Value)
{
TwitchChat.Connect();
}
}
}
internal static class Content
{
public static GameObject PluginCanvasPrefab { get; private set; }
public static GameObject MainThreadDispatcherPrefab { get; private set; }
public static void Load()
{
LoadAssetsFromAssetBundle();
}
private static void LoadAssetsFromAssetBundle()
{
AssetBundle val = LoadAssetBundle("twitchchatapi_assets");
if (!((Object)(object)val == (Object)null))
{
PluginCanvasPrefab = LoadAssetFromAssetBundle<GameObject>("TwitchChatAPICanvas", val);
MainThreadDispatcherPrefab = LoadAssetFromAssetBundle<GameObject>("MainThreadDispatcher", val);
Plugin.Logger.LogInfo((object)"Successfully loaded assets from AssetBundle!");
}
}
private static AssetBundle LoadAssetBundle(string fileName)
{
try
{
string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location);
string text = Path.Combine(directoryName, fileName);
return AssetBundle.LoadFromFile(text);
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to load AssetBundle \"{fileName}\". {arg}");
}
return null;
}
private static T LoadAssetFromAssetBundle<T>(string name, AssetBundle assetBundle) where T : Object
{
if (string.IsNullOrWhiteSpace(name))
{
Plugin.Logger.LogError((object)("Failed to load asset of type \"" + typeof(T).Name + "\" from AssetBundle. Name is null or whitespace."));
return default(T);
}
if ((Object)(object)assetBundle == (Object)null)
{
Plugin.Logger.LogError((object)("Failed to load asset of type \"" + typeof(T).Name + "\" with name \"" + name + "\" from AssetBundle. AssetBundle is null."));
return default(T);
}
T val = assetBundle.LoadAsset<T>(name);
if ((Object)(object)val == (Object)null)
{
Plugin.Logger.LogError((object)("Failed to load asset of type \"" + typeof(T).Name + "\" with name \"" + name + "\" from AssetBundle. No asset found with that type and name."));
return default(T);
}
return val;
}
}
[BepInPlugin("com.github.zehsteam.TwitchChatAPI", "TwitchChatAPI", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
internal class Plugin : BaseUnityPlugin
{
private readonly Harmony _harmony = new Harmony("com.github.zehsteam.TwitchChatAPI");
internal static Plugin Instance { get; private set; }
internal static ManualLogSource Logger { get; private set; }
internal static ConfigFile Config { get; private set; }
internal static ConfigManager ConfigManager { get; private set; }
private void Awake()
{
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
Logger = Logger.CreateLogSource("com.github.zehsteam.TwitchChatAPI");
Logger.LogInfo((object)"TwitchChatAPI has awoken!");
Config = Utils.CreateGlobalConfigFile();
_harmony.PatchAll(typeof(MenuManagerPatch));
_harmony.PatchAll(typeof(QuickMenuManagerPatch));
Content.Load();
ConfigManager = new ConfigManager();
if (ConfigManager.TwitchChat_Enabled.Value)
{
TwitchChat.Connect();
}
}
public void SpawnPluginCanvas()
{
if (!((Object)(object)PluginCanvas.Instance != (Object)null))
{
Object.Instantiate<GameObject>(Content.PluginCanvasPrefab);
Logger.LogInfo((object)"Spawned PluginCanvas");
}
}
public void SpawnMainThreadDispatcher()
{
if (!((Object)(object)MainThreadDispatcher.Instance != (Object)null))
{
Object.Instantiate<GameObject>(Content.MainThreadDispatcherPrefab);
Logger.LogInfo((object)"Spawned MainThreadDispatcher");
}
}
public void LogInfoExtended(object data)
{
LogExtended((LogLevel)16, data);
}
public void LogWarningExtended(object data)
{
LogExtended((LogLevel)4, data);
}
public void LogExtended(LogLevel level, object data)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
if (ConfigManager == null || ConfigManager.ExtendedLogging == null)
{
Logger.Log(level, data);
}
else if (ConfigManager.ExtendedLogging.Value)
{
Logger.Log(level, data);
}
}
}
internal static class TwitchChat
{
public const string ServerIP = "irc.chat.twitch.tv";
public const int ServerPort = 6667;
private static ConnectionState _connectionState;
private static TcpClient _client;
private static NetworkStream _stream;
private static StreamReader _reader;
private static StreamWriter _writer;
private static CancellationTokenSource _cts;
private static bool _isReconnecting;
private static Task _reconnectTask;
private static CancellationTokenSource _reconnectCts;
private static int _reconnectDelay;
private static bool _explicitDisconnect;
private static readonly object _connectionLock;
private static readonly SemaphoreSlim _readLock;
public static bool Enabled => Plugin.ConfigManager.TwitchChat_Enabled.Value;
public static string Channel => ("#" + Plugin.ConfigManager.TwitchChat_Channel.Value).Trim();
public static ConnectionState ConnectionState
{
get
{
return _connectionState;
}
private set
{
_connectionState = value;
PluginCanvas.Instance?.UpdateSettingsWindowConnectionStatus();
}
}
static TwitchChat()
{
_connectionState = ConnectionState.None;
_reconnectDelay = 5000;
_connectionLock = new object();
_readLock = new SemaphoreSlim(1, 1);
Application.quitting += OnApplicationQuit;
}
public static void Connect()
{
Task.Run((Func<Task?>)ConnectAsync);
}
public static async Task ConnectAsync()
{
lock (_connectionLock)
{
if (_isReconnecting)
{
CancelReconnect();
}
_explicitDisconnect = false;
_isReconnecting = false;
}
if (!Enabled)
{
Plugin.Logger.LogError((object)"Failed to connect to Twitch chat. Twitch chat has been disabled in the config settings.");
return;
}
if (ConnectionState == ConnectionState.Connecting)
{
Plugin.Logger.LogWarning((object)"Twitch chat is already connecting.");
return;
}
if (ConnectionState == ConnectionState.Connected)
{
Disconnect();
}
if (string.IsNullOrWhiteSpace(Channel) || Channel == "#")
{
Plugin.Logger.LogWarning((object)"Failed to start Twitch chat connection: Invalid or empty channel name.");
return;
}
Plugin.Logger.LogInfo((object)"Establishing connection to Twitch chat...");
ConnectionState = ConnectionState.Connecting;
try
{
_cts = new CancellationTokenSource();
_client = new TcpClient();
await _client.ConnectAsync("irc.chat.twitch.tv", 6667);
_stream = _client.GetStream();
_reader = new StreamReader(_stream);
_writer = new StreamWriter(_stream)
{
AutoFlush = true
};
await _writer.WriteLineAsync("NICK justinfan123");
await _writer.WriteLineAsync("CAP REQ :twitch.tv/tags");
await _writer.WriteLineAsync("CAP REQ :twitch.tv/commands");
await _writer.WriteLineAsync("JOIN " + Channel);
ConnectionState = ConnectionState.Connected;
_explicitDisconnect = false;
Plugin.Logger.LogInfo((object)("Successfully connected to Twitch chat " + Channel + "."));
API.InvokeOnConnect();
await Task.Run((Func<Task?>)ListenAsync, _cts.Token);
}
catch (Exception ex)
{
ConnectionState = ConnectionState.Disconnected;
_explicitDisconnect = false;
Plugin.Logger.LogError((object)$"Failed to connect to Twitch chat {Channel}. {ex}");
ScheduleReconnect();
}
}
public static void Disconnect()
{
lock (_connectionLock)
{
_explicitDisconnect = true;
CancelReconnect();
if (ConnectionState != ConnectionState.Connected && ConnectionState != ConnectionState.Connecting)
{
Plugin.Logger.LogInfo((object)"Twitch chat is not connected or already disconnecting.");
return;
}
ConnectionState = ConnectionState.Disconnecting;
_cts?.Cancel();
DisconnectStreams();
ConnectionState = ConnectionState.Disconnected;
Plugin.Logger.LogInfo((object)"Twitch chat connection stopped.");
API.InvokeOnDisconnect();
}
}
private static void DisconnectStreams()
{
_writer?.Dispose();
_reader?.Dispose();
_stream?.Dispose();
_client?.Close();
_writer = null;
_reader = null;
_stream = null;
_client = null;
}
private static void ScheduleReconnect()
{
lock (_connectionLock)
{
if (!Enabled || _explicitDisconnect || _isReconnecting)
{
return;
}
Plugin.Logger.LogInfo((object)$"Reconnection to Twitch chat will be attempted in {_reconnectDelay / 1000} seconds.");
_isReconnecting = true;
_reconnectCts = new CancellationTokenSource();
_reconnectTask = Task.Delay(_reconnectDelay, _reconnectCts.Token).ContinueWith((Func<Task, Task>)async delegate(Task task)
{
if (!task.IsCanceled)
{
lock (_connectionLock)
{
_isReconnecting = false;
}
if (Enabled)
{
Plugin.Logger.LogInfo((object)"Attempting to reconnect to Twitch chat...");
await ConnectAsync();
}
}
});
}
}
private static void CancelReconnect()
{
lock (_connectionLock)
{
if (_reconnectTask != null && !_reconnectTask.IsCompleted)
{
_reconnectCts?.Cancel();
_reconnectTask = null;
}
_isReconnecting = false;
}
}
private static async Task ListenAsync()
{
if (ConnectionState != ConnectionState.Connected || _reader == null)
{
return;
}
try
{
while (_cts != null && !_cts.Token.IsCancellationRequested)
{
lock (_connectionLock)
{
if (_reader == null)
{
break;
}
}
string message = await SafeReadLineAsync(_cts.Token);
if (message != null)
{
if (message.StartsWith("PING"))
{
Plugin.Instance.LogInfoExtended("Received PING, sending PONG...");
await (_writer?.WriteLineAsync("PONG :tmi.twitch.tv") ?? Task.CompletedTask).ConfigureAwait(continueOnCapturedContext: false);
}
else
{
MessageHelper.ProcessMessage(message);
}
}
}
}
catch (TaskCanceledException)
{
Plugin.Logger.LogInfo((object)"Twitch chat listen task canceled.");
}
catch (OperationCanceledException)
{
Plugin.Logger.LogInfo((object)"Twitch chat listen task canceled.");
}
catch (Exception ex4)
{
Exception ex = ex4;
Plugin.Logger.LogError((object)$"Twitch chat listen task failed. {ex}");
ScheduleReconnect();
}
finally
{
lock (_connectionLock)
{
ConnectionState = ConnectionState.Disconnected;
}
}
}
private static async Task<string> SafeReadLineAsync(CancellationToken cancellationToken)
{
await _readLock.WaitAsync(cancellationToken);
try
{
Task<string> readTask = _reader.ReadLineAsync();
if (await Task.WhenAny(new Task[2]
{
readTask,
Task.Delay(-1, cancellationToken)
}).ConfigureAwait(continueOnCapturedContext: false) == readTask)
{
return await readTask.ConfigureAwait(continueOnCapturedContext: false);
}
throw new OperationCanceledException(cancellationToken);
}
finally
{
_readLock.Release();
}
}
private static void OnApplicationQuit()
{
Plugin.Logger.LogInfo((object)"Application is quitting. Disconnecting Twitch chat...");
Disconnect();
}
}
internal static class Utils
{
public static string GetEnumName<T>(T e) where T : Enum
{
return Enum.GetName(typeof(T), e) ?? string.Empty;
}
public static string GetPluginDirectoryPath()
{
return Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location);
}
public static string GetConfigDirectoryPath()
{
return Paths.ConfigPath;
}
public static string GetGlobalConfigDirectoryPath()
{
return Path.Combine(Application.persistentDataPath, "TwitchChatAPI");
}
public static ConfigFile CreateConfigFile(string directoryPath, string name = null, bool saveOnInit = false)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected O, but got Unknown
BepInPlugin metadata = MetadataHelper.GetMetadata((object)Plugin.Instance);
if (name == null)
{
name = metadata.GUID;
}
name += ".cfg";
return new ConfigFile(Path.Combine(directoryPath, name), saveOnInit, metadata);
}
public static ConfigFile CreateLocalConfigFile(string name = null, bool saveOnInit = false)
{
if (name == null)
{
name = "com.github.zehsteam.TwitchChatAPI-" + name;
}
return CreateConfigFile(Paths.ConfigPath, name, saveOnInit);
}
public static ConfigFile CreateGlobalConfigFile(string name = null, bool saveOnInit = false)
{
if (name == null)
{
name = "global";
}
return CreateConfigFile(GetGlobalConfigDirectoryPath(), name, saveOnInit);
}
public static void LogStackTrace()
{
StackTrace stackTrace = new StackTrace();
for (int i = 1; i < stackTrace.FrameCount; i++)
{
StackFrame frame = stackTrace.GetFrame(i);
MethodBase method = frame.GetMethod();
Type declaringType = method.DeclaringType;
string name = method.Name;
Plugin.Instance.LogInfoExtended($"Call stack depth {i}: {declaringType}.{name}");
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "com.github.zehsteam.TwitchChatAPI";
public const string PLUGIN_NAME = "TwitchChatAPI";
public const string PLUGIN_VERSION = "1.0.1";
}
}
namespace com.github.zehsteam.TwitchChatAPI.Patches
{
[HarmonyPatch(typeof(MenuManager))]
internal static class MenuManagerPatch
{
[HarmonyPatch("Awake")]
[HarmonyPrefix]
private static void AwakePatch(MenuManager __instance)
{
MenuManagerHelper.SetInstance(__instance);
}
[HarmonyPatch("Start")]
[HarmonyPostfix]
private static void StartPatch(MenuManager __instance)
{
if (!__instance.isInitScene)
{
Plugin.Instance.SpawnPluginCanvas();
Plugin.Instance.SpawnMainThreadDispatcher();
}
}
}
[HarmonyPatch(typeof(QuickMenuManager))]
internal static class QuickMenuManagerPatch
{
[HarmonyPatch("Start")]
[HarmonyPrefix]
private static void StartPatch(QuickMenuManager __instance)
{
QuickMenuManagerHelper.SetInstance(__instance);
Plugin.Instance.SpawnPluginCanvas();
}
[HarmonyPatch("OpenQuickMenu")]
[HarmonyPostfix]
private static void OpenQuickMenuPatch()
{
PluginCanvas.Instance?.UpdateSettingsButton();
}
[HarmonyPatch("CloseQuickMenu")]
[HarmonyPostfix]
private static void CloseQuickMenuPatch()
{
PluginCanvas.Instance?.CloseSettingsWindow();
}
}
}
namespace com.github.zehsteam.TwitchChatAPI.Objects
{
internal class JsonSave
{
private JObject _data;
public string DirectoryPath { get; private set; }
public string FileName { get; private set; }
public string FilePath => Path.Combine(DirectoryPath, FileName);
public JsonSave(string directoryPath, string fileName)
{
DirectoryPath = directoryPath;
FileName = fileName;
_data = ReadFile();
}
public bool KeyExists(string key)
{
if (_data == null)
{
Plugin.Logger.LogError((object)"KeyExists: Data is null. Ensure the save file is properly loaded.");
return false;
}
return _data.ContainsKey(key);
}
public T LoadValue<T>(string key, T defaultValue = default(T))
{
if (TryLoadValue<T>(key, out var value))
{
return value;
}
return defaultValue;
}
public bool TryLoadValue<T>(string key, out T value)
{
//IL_0064: Expected O, but got Unknown
value = default(T);
if (_data == null)
{
Plugin.Logger.LogError((object)("LoadValue: Data is null. Returning default value for key: " + key + "."));
return false;
}
JToken val = default(JToken);
if (_data.TryGetValue(key, ref val))
{
try
{
value = val.ToObject<T>();
return true;
}
catch (JsonException val2)
{
JsonException val3 = val2;
Plugin.Logger.LogError((object)("LoadValue: JSON Conversion Error for key: " + key + ". " + ((Exception)(object)val3).Message));
}
catch (ArgumentNullException ex)
{
Plugin.Logger.LogError((object)("LoadValue: Argument Null Error for key: " + key + ". " + ex.Message));
}
catch (Exception ex2)
{
Plugin.Logger.LogError((object)("LoadValue: Unexpected Error for key: " + key + ". " + ex2.Message));
}
return false;
}
Plugin.Instance.LogWarningExtended("LoadValue: Key '" + key + "' does not exist. Returning default value.");
return false;
}
public bool SaveValue<T>(string key, T value)
{
if (_data == null)
{
Plugin.Logger.LogError((object)("SaveValue: Data is null. Cannot save key: " + key + "."));
return false;
}
try
{
JToken val = JToken.FromObject((object)value);
if (_data.ContainsKey(key))
{
_data[key] = val;
}
else
{
_data.Add(key, val);
}
return WriteFile(_data);
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("SaveValue: Error saving key: " + key + ". " + ex.Message));
return false;
}
}
private JObject ReadFile()
{
//IL_0080: Expected O, but got Unknown
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Expected O, but got Unknown
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
try
{
if (!File.Exists(FilePath))
{
Plugin.Logger.LogWarning((object)("ReadFile: Save file does not exist at \"" + FilePath + "\". Initializing with an empty file."));
return new JObject();
}
using FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
return JObject.Parse(streamReader.ReadToEnd());
}
catch (JsonException val)
{
JsonException val2 = val;
Plugin.Logger.LogError((object)("ReadFile: JSON Parsing Error for file: \"" + FilePath + "\". " + ((Exception)(object)val2).Message));
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("ReadFile: Unexpected Error for file: \"" + FilePath + "\". " + ex.Message));
}
return new JObject();
}
private bool WriteFile(JObject data)
{
try
{
if (!Directory.Exists(DirectoryPath))
{
Directory.CreateDirectory(DirectoryPath);
}
File.WriteAllText(FilePath, ((object)data).ToString(), Encoding.UTF8);
return true;
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("WriteFile: Unexpected Error for file: \"" + FilePath + "\". " + ex.Message));
}
return false;
}
}
public abstract class TwitchEvent
{
public string Channel { get; set; }
public TwitchUser User { get; set; }
public string Message { get; set; }
public Dictionary<string, string> Tags { get; set; }
}
public class TwitchSubEvent : TwitchEvent
{
public SubType SubType { get; set; }
public bool IsPrime { get; set; }
public int Tier { get; set; }
public int Months { get; set; }
public string RecipientUser { get; set; }
public int GiftCount { get; set; }
}
public class TwitchCheerEvent : TwitchEvent
{
public int CheerAmount { get; set; }
}
public class TwitchRaidEvent : TwitchEvent
{
public int ViewerCount { get; set; }
}
public struct TwitchMessage
{
public string Channel { get; set; }
public TwitchUser User { get; set; }
public string Message { get; set; }
public Dictionary<string, string> Tags { get; set; }
}
public struct TwitchRoomState
{
public string Channel { get; set; }
public bool IsEmoteOnly { get; set; }
public bool IsFollowersOnly { get; set; }
public bool IsR9K { get; set; }
public bool IsSlowMode { get; set; }
public bool IsSubsOnly { get; set; }
public Dictionary<string, string> Tags { get; set; }
}
public struct TwitchUser
{
public string UserId { get; set; }
public string DisplayName { get; set; }
public string Color { get; set; }
public bool IsVIP { get; set; }
public bool IsSubscriber { get; set; }
public bool IsModerator { get; set; }
public bool IsBroadcaster { get; set; }
}
}
namespace com.github.zehsteam.TwitchChatAPI.MonoBehaviours
{
public class MainThreadDispatcher : MonoBehaviour
{
private static readonly Queue<Action> _actions = new Queue<Action>();
public static MainThreadDispatcher Instance { get; private set; }
private void Awake()
{
if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
{
Object.Destroy((Object)(object)((Component)this).gameObject);
return;
}
Instance = this;
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
}
private void Update()
{
lock (_actions)
{
while (_actions.Count > 0)
{
_actions.Dequeue()?.Invoke();
}
}
}
public static void Enqueue(Action action)
{
lock (_actions)
{
_actions.Enqueue(action);
}
}
}
public class PluginCanvas : MonoBehaviour
{
[Header("Windows")]
[Space(5f)]
public GameObject SettingsWindowObject;
public GameObject MainMenuObject;
public GameObject QuickMenuObject;
[Header("Settings Window Properties")]
[Space(5f)]
public TextMeshProUGUI ConnectionStatusText;
public Toggle EnabledToggle;
public TMP_InputField ChannelInputField;
[Header("MainMenu Properties")]
[Space(5f)]
public RectTransform MainMenuSettingsButtonTransform;
[Header("QuickMenu Properties")]
[Space(5f)]
public RectTransform QuickMenuSettingsButtonTransform;
public static PluginCanvas Instance { get; private set; }
public bool IsSettingsWindowOpen { get; private set; }
private void Awake()
{
if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
{
Object.Destroy((Object)(object)((Component)this).gameObject);
}
else
{
Instance = this;
}
}
private void Start()
{
CloseSettingsWindow();
OpenSettingsWindowFirstTimeOnly();
UpdateSettingsButton();
}
private void OpenSettingsWindowFirstTimeOnly()
{
if (!SaveHelper.LoadValue("OpenedSettingsWindowFirstTime", SaveLocation.Global, defaultValue: false))
{
SaveHelper.SaveValue("OpenedSettingsWindowFirstTime", value: true, SaveLocation.Global);
OpenSettingsWindow();
}
}
public void OpenSettingsWindow()
{
if (!((Object)(object)SettingsWindowObject == (Object)null) && !IsSettingsWindowOpen && (!((Object)(object)QuickMenuManagerHelper.Instance != (Object)null) || QuickMenuManagerHelper.IsMenuOpen))
{
IsSettingsWindowOpen = true;
SettingsWindowObject.SetActive(true);
UpdateSettingsWindowUI();
UpdateSettingsButton();
}
}
public void CloseSettingsWindow()
{
if (!((Object)(object)SettingsWindowObject == (Object)null))
{
IsSettingsWindowOpen = false;
SettingsWindowObject.SetActive(false);
UpdateSettingsButton();
}
}
public void OnApplyButtonClicked()
{
if ((Object)(object)EnabledToggle != (Object)null)
{
Plugin.ConfigManager.TwitchChat_Enabled.Value = EnabledToggle.isOn;
}
if ((Object)(object)ChannelInputField != (Object)null)
{
Plugin.ConfigManager.TwitchChat_Channel.Value = ChannelInputField.text;
}
}
public void OnCloseButtonClicked()
{
CloseSettingsWindow();
}
private void UpdateSettingsWindowUI()
{
if ((Object)(object)EnabledToggle != (Object)null)
{
EnabledToggle.isOn = Plugin.ConfigManager.TwitchChat_Enabled.Value;
}
if ((Object)(object)ChannelInputField != (Object)null)
{
ChannelInputField.text = Plugin.ConfigManager.TwitchChat_Channel.Value;
}
UpdateSettingsWindowConnectionStatus();
}
public void UpdateSettingsWindowConnectionStatus()
{
if (!((Object)(object)ConnectionStatusText == (Object)null))
{
ConnectionState connectionState = TwitchChat.ConnectionState;
if (1 == 0)
{
}
string text = connectionState switch
{
ConnectionState.Connecting => "#00FF00",
ConnectionState.Connected => "#00FF00",
ConnectionState.Disconnecting => "#FF0000",
ConnectionState.Disconnected => "#FF0000",
_ => string.Empty,
};
if (1 == 0)
{
}
string text2 = text;
string text3 = (string.IsNullOrEmpty(text2) ? Utils.GetEnumName(TwitchChat.ConnectionState) : ("<color=" + text2 + ">" + Utils.GetEnumName(TwitchChat.ConnectionState) + "</color>"));
((TMP_Text)ConnectionStatusText).text = "Connection Status: " + text3;
}
}
public void OnSettingsButtonClicked()
{
OpenSettingsWindow();
}
public void UpdateSettingsButton()
{
UpdateMainMenuUI();
UpdateQuickMenuUI();
}
private void UpdateMainMenuUI()
{
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)MainMenuObject == (Object)null)
{
return;
}
MainMenuObject.SetActive((Object)(object)MenuManagerHelper.Instance != (Object)null);
if (!((Object)(object)MainMenuSettingsButtonTransform == (Object)null))
{
((Component)MainMenuSettingsButtonTransform).gameObject.SetActive(!IsSettingsWindowOpen);
if (MoreCompanyProxy.Enabled)
{
MainMenuSettingsButtonTransform.anchoredPosition = new Vector2(-145f, 48f);
}
else
{
MainMenuSettingsButtonTransform.anchoredPosition = new Vector2(-24f, 48f);
}
}
}
private void UpdateQuickMenuUI()
{
//IL_008e: 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)
if ((Object)(object)QuickMenuObject == (Object)null)
{
return;
}
QuickMenuObject.SetActive(QuickMenuManagerHelper.IsMenuOpen);
if (!((Object)(object)QuickMenuSettingsButtonTransform == (Object)null))
{
((Component)QuickMenuSettingsButtonTransform).gameObject.SetActive(!IsSettingsWindowOpen);
if (MoreCompanyProxy.Enabled)
{
QuickMenuSettingsButtonTransform.anchoredPosition = new Vector2(-169f, 63f);
}
else
{
QuickMenuSettingsButtonTransform.anchoredPosition = new Vector2(-52f, 63f);
}
}
}
}
}
namespace com.github.zehsteam.TwitchChatAPI.Helpers
{
internal static class ConfigHelper
{
public static void SkipAutoGen()
{
if (LethalConfigProxy.Enabled)
{
LethalConfigProxy.SkipAutoGen();
}
}
public static void AddButton(string section, string name, string description, string buttonText, Action callback)
{
if (LethalConfigProxy.Enabled)
{
LethalConfigProxy.AddButton(section, name, description, buttonText, callback);
}
}
public static ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, bool requiresRestart = false, AcceptableValueBase acceptableValues = null, Action<T> settingChanged = null, ConfigFile configFile = null)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Expected O, but got Unknown
if (configFile == null)
{
configFile = Plugin.Config;
}
ConfigEntry<T> configEntry = ((acceptableValues == null) ? configFile.Bind<T>(section, key, defaultValue, description) : configFile.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, Array.Empty<object>())));
if (settingChanged != null)
{
configEntry.SettingChanged += delegate
{
settingChanged?.Invoke(configEntry.Value);
};
}
if (LethalConfigProxy.Enabled)
{
LethalConfigProxy.AddConfig<T>(configEntry, requiresRestart);
}
return configEntry;
}
public static Dictionary<ConfigDefinition, string> GetOrphanedConfigEntries(ConfigFile configFile = null)
{
if (configFile == null)
{
configFile = Plugin.Config;
}
PropertyInfo property = ((object)configFile).GetType().GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic);
return (Dictionary<ConfigDefinition, string>)property.GetValue(configFile, null);
}
public static void SetConfigEntryValue<T>(ConfigEntry<T> configEntry, string value)
{
if (typeof(T) == typeof(int) && int.TryParse(value, out var result))
{
configEntry.Value = (T)(object)result;
return;
}
if (typeof(T) == typeof(float) && float.TryParse(value, out var result2))
{
configEntry.Value = (T)(object)result2;
return;
}
if (typeof(T) == typeof(double) && double.TryParse(value, out var result3))
{
configEntry.Value = (T)(object)result3;
return;
}
if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result4))
{
configEntry.Value = (T)(object)result4;
return;
}
if (typeof(T) == typeof(string))
{
configEntry.Value = (T)(object)value;
return;
}
throw new InvalidOperationException($"Unsupported type: {typeof(T)}");
}
public static void ClearUnusedEntries(ConfigFile configFile = null)
{
if (configFile == null)
{
configFile = Plugin.Config;
}
Dictionary<ConfigDefinition, string> orphanedConfigEntries = GetOrphanedConfigEntries(configFile);
if (orphanedConfigEntries != null)
{
orphanedConfigEntries.Clear();
configFile.Save();
}
}
}
internal static class MenuManagerHelper
{
public static MenuManager Instance { get; private set; }
public static void SetInstance(MenuManager menuManager)
{
Instance = menuManager;
}
}
internal static class MessageHelper
{
public static void ProcessMessage(string message)
{
try
{
if (message.StartsWith("@") && message.Contains("PRIVMSG"))
{
ProcessMessage_PRIVMSG(message);
}
else if (message.StartsWith("@") && message.Contains("USERNOTICE"))
{
ProcessMessage_USERNOTICE(message);
}
else if (message.StartsWith("@") && message.Contains("ROOMSTATE"))
{
ProcessMessage_ROOMSTATE(message);
}
else
{
Plugin.Instance.LogInfoExtended("Unhandled RAW message: " + message);
}
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_PRIVMSG(string message)
{
try
{
string text = message.Split(' ')[0].Substring(1);
Dictionary<string, string> dictionary = text.Split(';').ToDictionary((string tag) => tag.Split('=')[0], delegate(string tag)
{
string result;
if (!tag.Contains('='))
{
result = string.Empty;
}
else
{
int num3 = tag.IndexOf('=') + 1;
result = tag.Substring(num3, tag.Length - num3);
}
return result;
});
string text2 = message.Split("PRIVMSG")[1];
string channel = text2.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0].Trim().TrimStart('#');
string text3 = string.Empty;
int num = text2.IndexOf(':');
if (num != -1)
{
string text4 = text2;
int num2 = num + 1;
text3 = text4.Substring(num2, text4.Length - num2).Trim();
}
TwitchUser twitchUser = GetTwitchUser(channel, dictionary);
if (!twitchUser.Equals(null))
{
if (dictionary.ContainsKey("bits"))
{
ProcessMessage_PRIVMSG_Cheer(message, channel, twitchUser, text3, dictionary);
return;
}
TwitchMessage twitchMessage = default(TwitchMessage);
twitchMessage.Channel = channel;
twitchMessage.User = twitchUser;
twitchMessage.Message = text3;
twitchMessage.Tags = dictionary;
TwitchMessage message2 = twitchMessage;
API.InvokeOnMessage(message2);
}
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process PRIVMSG message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_PRIVMSG_Cheer(string message, string channel, TwitchUser twitchUser, string chatMessage, Dictionary<string, string> tags)
{
try
{
string[] value = new string[25]
{
"Cheer", "cheerwhal", "Corgo", "uni", "ShowLove", "Party", "SeemsGood", "Pride", "Kappa", "FrankerZ",
"HeyGuys", "DansGame", "EleGiggle", "TriHard", "Kreygasm", "4Head", "SwiftRage", "NotLikeThis", "FailFish", "VoHiYo",
"PJSalt", "MrDestructoid", "bday", "RIPCheer", "Shamrock"
};
string pattern = "\\b(" + string.Join("|", value) + ")\\d+\\b";
chatMessage = Regex.Replace(chatMessage, pattern, string.Empty, RegexOptions.IgnoreCase).Trim();
TwitchCheerEvent twitchCheerEvent = new TwitchCheerEvent
{
Channel = channel,
User = twitchUser,
Message = chatMessage,
Tags = tags,
CheerAmount = int.Parse(tags.GetValueOrDefault("bits", "0"))
};
Plugin.Instance.LogInfoExtended("RAW cheer message: " + message);
Plugin.Instance.LogInfoExtended($"[!] Cheer event: {twitchCheerEvent.User.DisplayName} cheered {twitchCheerEvent.CheerAmount} bits!\n{JsonConvert.SerializeObject((object)twitchCheerEvent, (Formatting)1)}");
API.InvokeOnCheer(twitchCheerEvent);
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process PRIVMSG message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_USERNOTICE(string message)
{
try
{
string text = message.Split(' ')[0].Substring(1);
Dictionary<string, string> dictionary = text.Split(';').ToDictionary((string tag) => tag.Split('=')[0], delegate(string tag)
{
string result;
if (!tag.Contains('='))
{
result = string.Empty;
}
else
{
int num3 = tag.IndexOf('=') + 1;
result = tag.Substring(num3, tag.Length - num3);
}
return result;
});
string valueOrDefault = dictionary.GetValueOrDefault("msg-id", string.Empty);
if (string.IsNullOrEmpty(valueOrDefault))
{
Plugin.Logger.LogError((object)("Failed to process USERNOTICE message:\n\n" + message));
return;
}
string text2 = message.Split("USERNOTICE")[1];
string channel = text2.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0].Trim().TrimStart('#');
string chatMessage = string.Empty;
int num = text2.IndexOf(':');
if (num != -1)
{
string text3 = text2;
int num2 = num + 1;
chatMessage = text3.Substring(num2, text3.Length - num2).Trim();
}
TwitchUser twitchUser = GetTwitchUser(channel, dictionary);
if (twitchUser.Equals(null))
{
return;
}
switch (valueOrDefault)
{
default:
if (!(valueOrDefault == "submysterygift"))
{
if (valueOrDefault == "raid")
{
ProcessMessage_USERNOTICE_Raid(message, channel, twitchUser, dictionary);
}
else
{
Plugin.Instance.LogInfoExtended("Unhandled USERNOTICE message: " + message);
}
break;
}
goto case "sub";
case "sub":
case "resub":
case "subgift":
ProcessMessage_USERNOTICE_Sub(message, channel, twitchUser, chatMessage, dictionary);
break;
}
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process USERNOTICE message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_USERNOTICE_Sub(string message, string channel, TwitchUser twitchUser, string chatMessage, Dictionary<string, string> tags)
{
try
{
string valueOrDefault = tags.GetValueOrDefault("msg-id", string.Empty);
if (string.IsNullOrEmpty(valueOrDefault))
{
Plugin.Logger.LogError((object)("Failed to process USERNOTICE message: " + message));
return;
}
SubType subType = SubType.Sub;
switch (valueOrDefault)
{
case "resub":
subType = SubType.Resub;
break;
case "subgift":
subType = SubType.SubGift;
break;
case "submysterygift":
subType = SubType.SubMysteryGift;
break;
}
if (subType == SubType.SubGift && tags.ContainsKey("msg-param-community-gift-id"))
{
Plugin.Instance.LogInfoExtended("Skipping subgift since it originates from a submysterygift. Message: " + message);
return;
}
bool isPrime = false;
int tier = 1;
if (tags.TryGetValue("msg-param-sub-plan", out var value) && !string.IsNullOrEmpty(value))
{
switch (value)
{
case "Prime":
isPrime = true;
break;
case "2000":
tier = 2;
break;
case "3000":
tier = 3;
break;
}
}
TwitchSubEvent twitchSubEvent = new TwitchSubEvent
{
Channel = channel,
User = twitchUser,
Message = chatMessage,
Tags = tags,
SubType = subType,
IsPrime = isPrime,
Tier = tier,
Months = int.Parse(tags.GetValueOrDefault("msg-param-cumulative-months", "0")),
RecipientUser = tags.GetValueOrDefault("msg-param-recipient-display-name", string.Empty),
GiftCount = int.Parse(tags.GetValueOrDefault("msg-param-mass-gift-count", "0"))
};
Plugin.Instance.LogInfoExtended("RAW subscription message: " + message);
Plugin.Instance.LogInfoExtended("[!] Subscription event: \n" + JsonConvert.SerializeObject((object)twitchSubEvent, (Formatting)1));
API.InvokeOnSub(twitchSubEvent);
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process USERNOTICE message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_USERNOTICE_Raid(string message, string channel, TwitchUser twitchUser, Dictionary<string, string> tags)
{
try
{
TwitchRaidEvent twitchRaidEvent = new TwitchRaidEvent
{
Channel = channel,
User = twitchUser,
Message = string.Empty,
Tags = tags,
ViewerCount = int.Parse(tags.GetValueOrDefault("msg-param-viewerCount", "0"))
};
Plugin.Instance.LogInfoExtended("RAW raid message: " + message);
Plugin.Instance.LogInfoExtended($"[!] Raid detected: {twitchRaidEvent.User.DisplayName} is raiding with {twitchRaidEvent.ViewerCount} viewers!\n{JsonConvert.SerializeObject((object)twitchRaidEvent, (Formatting)1)}");
API.InvokeOnRaid(twitchRaidEvent);
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process USERNOTICE message:\n\n{message}\n\nError: {arg}");
}
}
private static void ProcessMessage_ROOMSTATE(string message)
{
try
{
string text = message.Split(' ')[0].Substring(1);
Dictionary<string, string> dictionary = text.Split(';').ToDictionary((string tag) => tag.Split('=')[0], (string tag) => tag.Contains('=') ? tag.Split('=')[1] : "");
string channel = message.Split("ROOMSTATE")[1].Trim().TrimStart('#');
TwitchRoomState twitchRoomState = default(TwitchRoomState);
twitchRoomState.Channel = channel;
twitchRoomState.IsEmoteOnly = dictionary.ContainsKey("emote-only") && dictionary["emote-only"] == "1";
twitchRoomState.IsFollowersOnly = dictionary.ContainsKey("followers-only") && dictionary["followers-only"] != "-1";
twitchRoomState.IsR9K = dictionary.ContainsKey("r9k") && dictionary["r9k"] == "1";
twitchRoomState.IsSlowMode = dictionary.ContainsKey("slow") && dictionary["slow"] != "0";
twitchRoomState.IsSubsOnly = dictionary.ContainsKey("subs-only") && dictionary["subs-only"] == "1";
twitchRoomState.Tags = dictionary;
TwitchRoomState twitchRoomState2 = twitchRoomState;
Plugin.Instance.LogInfoExtended("RAW roomstate message: " + message);
Plugin.Instance.LogInfoExtended("[!] Room state change detected: \n" + JsonConvert.SerializeObject((object)twitchRoomState2, (Formatting)1));
API.InvokeOnRoomStateUpdate(twitchRoomState2);
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to process ROOMSTATE message:\n\n{message}\n\nError: {arg}");
}
}
private static TwitchUser GetTwitchUser(string channel, Dictionary<string, string> tags)
{
try
{
string valueOrDefault = tags.GetValueOrDefault("display-name", "Anonymous");
TwitchUser result = default(TwitchUser);
result.UserId = tags.GetValueOrDefault("user-id", "0");
result.DisplayName = valueOrDefault;
result.Color = tags.GetValueOrDefault("color", "#FFFFFF");
result.IsVIP = tags.TryGetValue("vip", out var value) && value == "1";
result.IsSubscriber = tags.TryGetValue("subscriber", out var value2) && value2 == "1";
result.IsModerator = tags.TryGetValue("mod", out var value3) && value3 == "1";
result.IsBroadcaster = valueOrDefault.Equals(channel, StringComparison.OrdinalIgnoreCase);
return result;
}
catch (Exception arg)
{
Plugin.Logger.LogError((object)$"Failed to get TwitchUser: {arg}");
return default(TwitchUser);
}
}
}
internal static class QuickMenuManagerHelper
{
public static QuickMenuManager Instance { get; private set; }
public static bool IsMenuOpen => (Object)(object)Instance != (Object)null && Instance.isMenuOpen;
public static void SetInstance(QuickMenuManager quickMenuManager)
{
Instance = quickMenuManager;
}
}
internal static class SaveHelper
{
private static JsonSave _modpackSave;
private static JsonSave _globalSave;
static SaveHelper()
{
_modpackSave = new JsonSave(Utils.GetConfigDirectoryPath(), "TwitchChatAPI_Save");
_globalSave = new JsonSave(Utils.GetGlobalConfigDirectoryPath(), "GlobalSave");
}
public static bool KeyExists(string key, SaveLocation saveLocation)
{
try
{
string key2 = GetKey(key, saveLocation);
switch (saveLocation)
{
case SaveLocation.CurrentSave:
return ES3.KeyExists(key2, GetCurrentSaveFilePath());
case SaveLocation.GeneralSave:
return ES3.KeyExists(key2, GetGeneralSaveFilePath());
case SaveLocation.Modpack:
return _modpackSave.KeyExists(key2);
case SaveLocation.Global:
return _globalSave.KeyExists(key2);
default:
Plugin.Logger.LogWarning((object)$"KeyExists: Unknown SaveLocation: {saveLocation}");
return false;
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)$"KeyExists Error: Key: \"{key}\", SaveLocation: {saveLocation}. Exception: {ex.Message}");
return false;
}
}
public static T LoadValue<T>(string key, SaveLocation saveLocation, T defaultValue = default(T))
{
if (TryLoadValue<T>(key, saveLocation, out var value))
{
return value;
}
return defaultValue;
}
public static bool TryLoadValue<T>(string key, SaveLocation saveLocation, out T value)
{
value = default(T);
if (!KeyExists(key, saveLocation))
{
return false;
}
try
{
string key2 = GetKey(key, saveLocation);
switch (saveLocation)
{
case SaveLocation.CurrentSave:
value = ES3.Load<T>(key2, GetCurrentSaveFilePath(), default(T));
return true;
case SaveLocation.GeneralSave:
value = ES3.Load<T>(key2, GetGeneralSaveFilePath(), default(T));
return true;
case SaveLocation.Modpack:
return _modpackSave.TryLoadValue<T>(key2, out value);
case SaveLocation.Global:
return _globalSave.TryLoadValue<T>(key2, out value);
default:
Plugin.Logger.LogWarning((object)$"LoadValue: Unknown SaveLocation: {saveLocation}");
return false;
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)$"LoadValue Error: Key: \"{key}\", SaveLocation: {saveLocation}. Exception: {ex.Message}");
return false;
}
}
public static bool SaveValue<T>(string key, T value, SaveLocation saveLocation)
{
if (string.IsNullOrWhiteSpace(key) || value == null)
{
Plugin.Logger.LogError((object)"SaveValue: Invalid key or value.");
return false;
}
try
{
string key2 = GetKey(key, saveLocation);
switch (saveLocation)
{
case SaveLocation.CurrentSave:
ES3.Save<T>(key2, value, GetCurrentSaveFilePath());
return true;
case SaveLocation.GeneralSave:
ES3.Save<T>(key2, value, GetGeneralSaveFilePath());
return true;
case SaveLocation.Modpack:
return _modpackSave.SaveValue(key2, value);
case SaveLocation.Global:
return _globalSave.SaveValue(key2, value);
default:
Plugin.Logger.LogWarning((object)$"SaveValue: Unknown SaveLocation: {saveLocation}");
return false;
}
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)$"SaveValue Error: Key: \"{key}\", SaveLocation: {saveLocation}. Exception: {ex.Message}");
return false;
}
}
private static string GetKey(string key, SaveLocation saveLocation)
{
if (1 == 0)
{
}
string result = (((uint)saveLocation > 1u) ? key : ("com.github.zehsteam.TwitchChatAPI." + key));
if (1 == 0)
{
}
return result;
}
private static string GetCurrentSaveFilePath()
{
if ((Object)(object)GameNetworkManager.Instance == (Object)null)
{
Plugin.Logger.LogWarning((object)"GetCurrentSaveFilePath: GameNetworkManager instance is null. Returning an empty string.");
return string.Empty;
}
return GameNetworkManager.Instance.currentSaveFileName ?? string.Empty;
}
private static string GetGeneralSaveFilePath()
{
return "LCGeneralSaveData";
}
}
}
namespace com.github.zehsteam.TwitchChatAPI.Enums
{
public enum ConnectionState
{
None,
Connecting,
Connected,
Disconnecting,
Disconnected
}
internal enum SaveLocation
{
CurrentSave,
GeneralSave,
Modpack,
Global
}
public enum SubType
{
Sub,
Resub,
SubGift,
SubMysteryGift
}
}
namespace com.github.zehsteam.TwitchChatAPI.Dependencies
{
internal static class LethalConfigProxy
{
public const string PLUGIN_GUID = "ainavt.lc.lethalconfig";
private static bool? _enabled;
public static bool Enabled
{
get
{
bool valueOrDefault = _enabled.GetValueOrDefault();
if (!_enabled.HasValue)
{
valueOrDefault = Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig");
_enabled = valueOrDefault;
}
return _enabled.Value;
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void SkipAutoGen()
{
LethalConfigManager.SkipAutoGen();
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void AddConfig<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
{
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Expected O, but got Unknown
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Expected O, but got Unknown
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Expected O, but got Unknown
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Expected O, but got Unknown
AcceptableValueBase acceptableValues = ((ConfigEntryBase)configEntry).Description.AcceptableValues;
if (acceptableValues != null)
{
if (acceptableValues is AcceptableValueRange<float> || acceptableValues is AcceptableValueRange<int>)
{
AddConfigSlider<T>(configEntry, requiresRestart);
return;
}
if (acceptableValues is AcceptableValueList<string>)
{
AddConfigDropdown<T>(configEntry, requiresRestart);
return;
}
}
if (!(configEntry is ConfigEntry<string> val))
{
if (!(configEntry is ConfigEntry<bool> val2))
{
if (!(configEntry is ConfigEntry<float> val3))
{
if (!(configEntry is ConfigEntry<int> val4))
{
throw new NotSupportedException($"Unsupported type: {typeof(T)}");
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new IntInputFieldConfigItem(val4, requiresRestart));
}
else
{
LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatInputFieldConfigItem(val3, requiresRestart));
}
}
else
{
LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(val2, requiresRestart));
}
}
else
{
LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(val, requiresRestart));
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void AddConfigSlider<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
if (!(configEntry is ConfigEntry<float> val))
{
if (!(configEntry is ConfigEntry<int> val2))
{
throw new NotSupportedException($"Slider not supported for type: {typeof(T)}");
}
LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(val2, requiresRestart));
}
else
{
LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(val, requiresRestart));
}
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void AddConfigDropdown<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
if (configEntry is ConfigEntry<string> val)
{
LethalConfigManager.AddConfigItem((BaseConfigItem)new TextDropDownConfigItem(val, requiresRestart));
return;
}
throw new NotSupportedException($"Dropdown not supported for type: {typeof(T)}");
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void AddButton(string section, string name, string description, string buttonText, Action callback)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Expected O, but got Unknown
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected O, but got Unknown
LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem(section, name, description, buttonText, (GenericButtonHandler)delegate
{
callback?.Invoke();
}));
}
}
internal static class MoreCompanyProxy
{
public const string PLUGIN_GUID = "me.swipez.melonloader.morecompany";
private static bool? _enabled;
public static bool Enabled
{
get
{
bool valueOrDefault = _enabled.GetValueOrDefault();
if (!_enabled.HasValue)
{
valueOrDefault = Chainloader.PluginInfos.ContainsKey("me.swipez.melonloader.morecompany");
_enabled = valueOrDefault;
}
return _enabled.Value;
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}