using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Lexone.UnityTwitchChat;
using Microsoft.CodeAnalysis;
using Mirror;
using SBG_Chat_Integration.Actions;
using SBG_Chat_Integration.UI;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("GameAssembly")]
[assembly: IgnoresAccessChecksTo("SharedAssembly")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.atomic.sbgchatintegration")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("com.atomic.sbgchatintegration")]
[assembly: AssemblyTitle("SBG_Chat_Integration")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[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 BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace Microsoft.CodeAnalysis
{
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace Lexone.UnityTwitchChat
{
[Serializable]
public class Chatter
{
public string login;
public string channel;
public string message;
public IRCTags tags;
public Chatter(string login, string channel, string message, IRCTags tags)
{
this.login = login;
this.channel = channel;
this.message = message;
this.tags = tags;
}
public Color GetNameColor(bool normalize = true)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
Color val = default(Color);
if (ColorUtility.TryParseHtmlString(tags.colorHex, ref val))
{
if (normalize)
{
return ChatColors.NormalizeColor(val);
}
return val;
}
return Color.white;
}
public bool IsDisplayNameFontSafe()
{
return ParseHelper.CheckNameRegex(tags.displayName);
}
public bool ContainsEmote(string emoteId)
{
return tags.ContainsEmote(emoteId);
}
public bool HasBadge(string badgeName)
{
return tags.HasBadge(badgeName);
}
}
public static class Extensions
{
public static void WriteLine(this NetworkStream stream, string output, bool showDebug = false)
{
if (showDebug)
{
Debug.Log((object)(Tags.write + " " + output));
}
byte[] bytes = Encoding.UTF8.GetBytes(output);
stream.Write(bytes, 0, bytes.Length);
stream.WriteByte(13);
stream.WriteByte(10);
stream.Flush();
}
public static string GetDescription(this IRCReply alert)
{
return alert switch
{
IRCReply.CONNECTED_TO_SERVER => "Connected to IRC",
IRCReply.PONG_RECEIVED => "Pong!",
IRCReply.JOINED_CHANNEL => "Joined channel",
IRCReply.MISSING_LOGIN_INFO => "Missing login information (OAuth or username)",
IRCReply.BAD_LOGIN => "Login failed",
IRCReply.CONNECTION_INTERRUPTED => "Connection to IRC interrupted",
IRCReply.NO_CONNECTION => "Connection to IRC failed",
_ => "Unknown alert",
};
}
}
[AddComponentMenu("Unity Twitch Chat/Twitch IRC")]
public class IRC : MonoBehaviour
{
[CompilerGenerated]
private sealed class <<Connect>g__StartConnection|40_0>d : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public IRC <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <<Connect>g__StartConnection|40_0>d(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Expected O, but got Unknown
int num = <>1__state;
IRC iRC = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
if (iRC.connection != null)
{
<>2__current = ((MonoBehaviour)iRC).StartCoroutine(iRC.NonBlockingDisconnect());
<>1__state = 1;
return true;
}
goto IL_0053;
case 1:
<>1__state = -1;
goto IL_0053;
case 2:
{
<>1__state = -1;
break;
}
IL_0053:
iRC.connection = new TwitchConnection(iRC);
if (iRC.connection.tcpClient == null || !iRC.connection.tcpClient.Connected)
{
iRC.alertQueue.Enqueue(IRCReply.NO_CONNECTION);
return false;
}
if (iRC.connectionFailCount >= 2)
{
int num2 = 1 << iRC.connectionFailCount - 2;
if (iRC.showIRCDebug)
{
Debug.Log((object)$"{Tags.alert} Reconnecting in {num2} seconds");
}
<>2__current = (object)new WaitForSecondsRealtime((float)num2);
<>1__state = 2;
return true;
}
break;
}
iRC.connection.Begin();
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <NonBlockingDisconnect>d__42 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public IRC <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <NonBlockingDisconnect>d__42(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
IRC iRC = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = ((MonoBehaviour)iRC).StartCoroutine(iRC.connection.End());
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
iRC.connection = null;
if (iRC.showIRCDebug)
{
Debug.Log((object)(Tags.alert + " Disconnected from Twitch IRC"));
}
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[Header("Twitch IRC address and port")]
[SerializeField]
public string address = "irc.chat.twitch.tv";
[SerializeField]
public int port = 6667;
[Header("Twitch IRC connection")]
[Tooltip("If true, the client will connect to Twitch IRC anonymously (OAuth and username will be ignored)\n\nNote that you can't send chat messages when using anonymous login.")]
[SerializeField]
private bool useAnonymousLogin;
[Tooltip("The OAuth token which will be used to authenticate with Twitch.\n\nGenerate one at: https://twitchapps.com/tmi/")]
[SerializeField]
public string oauth = "";
[Tooltip("The Twitch username which will be used to authenticate with Twitch IRC.\n\n(this is the login name, not display name)")]
[SerializeField]
public string username = "";
[Tooltip("The Twitch channel name which the client will join.")]
[SerializeField]
public string channel = "";
[Header("General settings")]
[Tooltip("If true, the client will connect to Twitch IRC on Start.")]
[SerializeField]
private bool connectIRCOnStart = true;
[Tooltip("If true, the client will automatically join the given channel upon a successful connection.")]
[SerializeField]
public bool joinChannelOnStart = true;
[Tooltip("If true, IRC will be set to DontDestroyOnLoad and any duplicate instances will be removed.")]
[SerializeField]
public bool dontDestroyOnLoad = true;
[Tooltip("If true, every IRC message sent and received will be logged to the console.")]
[SerializeField]
public bool showIRCDebug = true;
[Tooltip("If true, the thread start and stop will be logged to the console.")]
[SerializeField]
public bool showThreadDebug = true;
[Tooltip("If true, chatters who haven't set their name color on Twitch will be assigned a random color, instead of white.")]
[SerializeField]
public bool useRandomColorForUndefined;
[Header("Chat read settings (read thread)")]
[Tooltip("The number of milliseconds between each time the read thread checks for new messages.")]
[SerializeField]
public int readInterval = 50;
[Tooltip("The capacity of the read buffer. Smaller values consume less memory but require more cycles to retrieve data (CPU usage)")]
[SerializeField]
public ReadBufferSize readBufferSize = ReadBufferSize._256;
[Header("Chat write settings (write thread)")]
[Tooltip("The number of milliseconds between each time the write thread checks its queues.")]
public int writeInterval = 50;
private static readonly int maxDataPerFrame = 100;
private int connectionFailCount;
private TwitchConnection connection;
internal readonly ConcurrentQueue<IRCReply> alertQueue = new ConcurrentQueue<IRCReply>();
internal readonly ConcurrentQueue<Chatter> chatterQueue = new ConcurrentQueue<Chatter>();
public static IRC Instance { get; private set; }
public IRCTags ClientUserTags => connection?.ClientUserTags;
public event Action<Chatter> OnChatMessage;
public event Action<IRCReply> OnConnectionAlert;
[ContextMenu("Ping")]
public void Ping()
{
connection?.Ping();
}
private void Awake()
{
if (Object.op_Implicit((Object)(object)Instance))
{
if (dontDestroyOnLoad)
{
((Component)this).gameObject.SetActive(false);
Object.Destroy((Object)(object)((Component)this).gameObject);
}
}
else
{
Instance = this;
if (dontDestroyOnLoad)
{
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
}
}
}
private void Start()
{
if (connectIRCOnStart)
{
Connect();
}
}
private void Update()
{
HandlePendingInformation();
}
private void OnDestroy()
{
if (dontDestroyOnLoad && (Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
BlockingDisconnect();
}
private void OnDisable()
{
BlockingDisconnect();
}
private void HandlePendingInformation()
{
int num = 0;
while (!alertQueue.IsEmpty && num < maxDataPerFrame)
{
if (alertQueue.TryDequeue(out var result))
{
HandleConnectionAlert(result);
num++;
}
}
while (!chatterQueue.IsEmpty && num < maxDataPerFrame)
{
if (chatterQueue.TryDequeue(out Chatter result2))
{
this.OnChatMessage?.Invoke(result2);
num++;
}
}
}
private void HandleConnectionAlert(IRCReply alert)
{
if (showIRCDebug)
{
Debug.Log((object)(Tags.alert + " " + alert.GetDescription()));
}
switch (alert)
{
case IRCReply.MISSING_LOGIN_INFO:
case IRCReply.BAD_LOGIN:
case IRCReply.NO_CONNECTION:
connectionFailCount = 0;
Disconnect();
break;
case IRCReply.CONNECTION_INTERRUPTED:
connectionFailCount++;
Connect();
break;
case IRCReply.JOINED_CHANNEL:
connectionFailCount = 0;
break;
}
this.OnConnectionAlert?.Invoke(alert);
}
[ContextMenu("Connect")]
public void Connect()
{
if (useAnonymousLogin)
{
username = "justinfan" + Random.Range(1000, 9999);
oauth = "";
}
else
{
if (oauth.Length <= 0 || username.Length <= 0)
{
alertQueue.Enqueue(IRCReply.MISSING_LOGIN_INFO);
return;
}
if (oauth.StartsWith("oauth:"))
{
oauth = oauth.Substring(6);
}
}
((MonoBehaviour)this).StartCoroutine(StartConnection());
[IteratorStateMachine(typeof(<<Connect>g__StartConnection|40_0>d))]
IEnumerator StartConnection()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <<Connect>g__StartConnection|40_0>d(0)
{
<>4__this = this
};
}
}
[ContextMenu("Disconnect")]
public void Disconnect()
{
if (connection != null && !connection.disconnectCalled)
{
((MonoBehaviour)this).StartCoroutine(NonBlockingDisconnect());
}
}
[IteratorStateMachine(typeof(<NonBlockingDisconnect>d__42))]
private IEnumerator NonBlockingDisconnect()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <NonBlockingDisconnect>d__42(0)
{
<>4__this = this
};
}
private void BlockingDisconnect()
{
if (connection != null)
{
connection.BlockingEnd();
connection = null;
if (showIRCDebug)
{
Debug.Log((object)(Tags.alert + " Disconnected from Twitch IRC"));
}
}
}
public void SendChatMessage(string message)
{
if (useAnonymousLogin)
{
Debug.LogError((object)"Chat messages cannot be sent with anonymous login");
}
else
{
connection.SendChatMessage(message);
}
}
public void JoinChannel(string channel)
{
if (channel == "")
{
Debug.LogError((object)"Failed joining channel. Channel name is empty");
}
else
{
connection.SendCommand("JOIN #" + channel.ToLower(), priority: true);
}
}
public void LeaveChannel(string channel)
{
if (channel == "")
{
Debug.LogError((object)"Failed leaving channel. Channel name is empty");
}
else
{
connection.SendCommand("PART #" + channel.ToLower(), priority: true);
}
}
}
[Serializable]
public struct ChatterEmote
{
[Serializable]
public struct Index
{
public int startIndex;
public int endIndex;
}
public string id;
public Index[] indexes;
}
[Serializable]
public struct ChatterBadge
{
public string id;
public string version;
}
[Serializable]
public class IRCTags
{
public string colorHex = string.Empty;
public string displayName = string.Empty;
public string channelId = string.Empty;
public string userId = string.Empty;
public ChatterBadge[] badges = new ChatterBadge[0];
public ChatterEmote[] emotes = new ChatterEmote[0];
public bool ContainsEmote(string emoteId)
{
ChatterEmote[] array = emotes;
for (int i = 0; i < array.Length; i++)
{
ChatterEmote chatterEmote = array[i];
if (chatterEmote.id == emoteId)
{
return true;
}
}
return false;
}
public bool HasBadge(string badge)
{
ChatterBadge[] array = badges;
for (int i = 0; i < array.Length; i++)
{
ChatterBadge chatterBadge = array[i];
if (chatterBadge.id == badge)
{
return true;
}
}
return false;
}
}
internal static class ParseHelper
{
private static Regex symbolRegex = new Regex("^[a-zA-Z0-9_]+$", RegexOptions.Compiled);
public static int IndexOfNth(this string source, char val, int nth = 0)
{
int num = source.IndexOf(val);
for (int i = 0; i < nth; i++)
{
if (num == -1)
{
return -1;
}
num = source.IndexOf(val, num + 1);
}
return num;
}
public static bool CheckNameRegex(string displayName)
{
return symbolRegex.IsMatch(displayName);
}
public static IRCTags ParseTags(string tagString)
{
IRCTags iRCTags = new IRCTags();
string[] array = tagString.Split(';');
for (int i = 0; i < array.Length; i++)
{
string text = array[i].Substring(array[i].IndexOf('=') + 1);
if (text.Length > 0)
{
switch (array[i].Substring(0, array[i].IndexOf('=')))
{
case "badges":
iRCTags.badges = ParseBadges(text.Split(','));
break;
case "color":
iRCTags.colorHex = text;
break;
case "display-name":
iRCTags.displayName = text;
break;
case "emotes":
iRCTags.emotes = ParseTwitchEmotes(text.Split('/'));
break;
case "room-id":
iRCTags.channelId = text;
break;
case "user-id":
iRCTags.userId = text;
break;
}
}
}
return iRCTags;
}
public static string ParseLoginName(string ircString)
{
return ircString.Substring(1, ircString.IndexOf('!') - 1);
}
public static string ParseChannel(string ircString)
{
string text = ircString.Substring(ircString.IndexOf('#') + 1);
int num = text.IndexOf(' ');
if (num == -1)
{
return text;
}
return text.Substring(0, num);
}
public static string ParseMessage(string ircString)
{
return ircString.Substring(ircString.IndexOfNth(' ', 2) + 2);
}
public static ChatterEmote[] ParseTwitchEmotes(string[] splitEmotes)
{
ChatterEmote[] array = new ChatterEmote[splitEmotes.Length];
for (int i = 0; i < splitEmotes.Length; i++)
{
string text = splitEmotes[i];
string[] array2 = ((text.Substring(text.IndexOf(':') + 1).Length > 0) ? text.Substring(text.IndexOf(':') + 1).Split(',') : new string[0]);
ChatterEmote.Index[] array3 = new ChatterEmote.Index[array2.Length];
for (int j = 0; j < array3.Length; j++)
{
array3[j].startIndex = int.Parse(array2[j].Substring(0, array2[j].IndexOf('-')));
array3[j].endIndex = int.Parse(array2[j].Substring(array2[j].IndexOf('-') + 1));
}
array[i] = new ChatterEmote
{
id = text.Substring(0, text.IndexOf(':')),
indexes = array3
};
}
return array;
}
public static ChatterBadge[] ParseBadges(string[] splitBadges)
{
ChatterBadge[] array = new ChatterBadge[splitBadges.Length];
for (int i = 0; i < splitBadges.Length; i++)
{
string text = splitBadges[i];
array[i].id = text.Substring(0, text.IndexOf('/'));
array[i].version = text.Substring(text.IndexOf('/') + 1);
}
return array;
}
}
public struct RateLimit
{
public int count;
public TimeSpan timeSpan;
public static readonly RateLimit ChatRegular = new RateLimit(20, new TimeSpan(0, 0, 30));
public static readonly RateLimit ChatModerator = new RateLimit(100, new TimeSpan(0, 0, 30));
public static readonly RateLimit SiteLimitVerified = new RateLimit(7500, new TimeSpan(0, 0, 30));
public static readonly RateLimit AuthAttemptsRegular = new RateLimit(20, new TimeSpan(0, 0, 10));
public static readonly RateLimit JoinAttemptsRegular = new RateLimit(20, new TimeSpan(0, 0, 10));
public static readonly RateLimit AuthAttemptsVerified = new RateLimit(200, new TimeSpan(0, 0, 10));
public static readonly RateLimit JoinAttemptsVerified = new RateLimit(2000, new TimeSpan(0, 0, 10));
public static readonly RateLimit WhispersA = new RateLimit(3, new TimeSpan(0, 0, 1));
public static readonly RateLimit WhispersB = new RateLimit(100, new TimeSpan(0, 1, 0));
public static readonly RateLimit WhisperChannels = new RateLimit(40, new TimeSpan(1, 0, 0, 0));
public RateLimit(int count, TimeSpan timeSpan)
{
this.count = count;
this.timeSpan = timeSpan;
}
}
internal class TwitchConnection
{
[CompilerGenerated]
private sealed class <End>d__31 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public TwitchConnection <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <End>d__31(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
TwitchConnection twitchConnection = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
if (twitchConnection.tcpClient == null || twitchConnection.disconnectCalled)
{
return false;
}
twitchConnection.disconnectCalled = true;
twitchConnection.ThreadsRunning = false;
goto IL_0062;
case 1:
<>1__state = -1;
goto IL_0062;
case 2:
{
<>1__state = -1;
break;
}
IL_0062:
if (twitchConnection.readThread.IsAlive)
{
<>2__current = null;
<>1__state = 1;
return true;
}
break;
}
if (twitchConnection.writeThread.IsAlive)
{
<>2__current = null;
<>1__state = 2;
return true;
}
twitchConnection.tcpClient.Close();
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private IRCTags _clientUserTags;
private int _threadsRunning = 1;
public bool disconnectCalled;
private readonly string oauth;
private readonly string nick;
private readonly string channel;
private readonly int readBufferSize;
private readonly int readInterval;
private readonly int writeInterval;
private readonly bool joinChannelOnStart;
private readonly bool showIRCDebug;
private readonly bool showThreadDebug;
private readonly bool useRandomColorForUndefined;
private readonly ConcurrentQueue<IRCReply> alertQueue;
private readonly ConcurrentQueue<Chatter> chatterQueue;
private Thread readThread;
private Thread writeThread;
private RateLimit rateLimit = RateLimit.ChatRegular;
private object rateLimitLock = new object();
private int sessionRandom = DateTime.Now.Second;
private ConcurrentQueue<string> priorityWriteQueue = new ConcurrentQueue<string>();
private ConcurrentQueue<string> writeQueue = new ConcurrentQueue<string>();
private ConcurrentQueue<DateTime> writeTimestamps = new ConcurrentQueue<DateTime>();
public TcpClient tcpClient { get; private set; }
public IRCTags ClientUserTags
{
get
{
return _clientUserTags;
}
set
{
Interlocked.Exchange(ref _clientUserTags, value);
}
}
private bool ThreadsRunning
{
get
{
return _threadsRunning == 1;
}
set
{
Interlocked.Exchange(ref _threadsRunning, value ? 1 : 0);
}
}
public TwitchConnection(IRC irc)
{
try
{
tcpClient = new TcpClient(irc.address, irc.port);
}
catch
{
tcpClient = null;
}
oauth = irc.oauth;
nick = irc.username;
channel = irc.channel;
readBufferSize = (int)irc.readBufferSize;
readInterval = irc.readInterval;
writeInterval = irc.writeInterval;
alertQueue = irc.alertQueue;
chatterQueue = irc.chatterQueue;
rateLimit = RateLimit.ChatRegular;
joinChannelOnStart = irc.joinChannelOnStart;
showIRCDebug = irc.showIRCDebug;
showThreadDebug = irc.showThreadDebug;
useRandomColorForUndefined = irc.useRandomColorForUndefined;
}
public void Begin()
{
readThread = new Thread((ThreadStart)delegate
{
ReadThreadLoop();
});
writeThread = new Thread((ThreadStart)delegate
{
WriteThreadLoop();
});
readThread.Start();
writeThread.Start();
SendCommand("PASS oauth:" + oauth.ToLower(), priority: true);
SendCommand("NICK " + nick.ToLower(), priority: true);
SendCommand("CAP REQ :twitch.tv/tags twitch.tv/commands", priority: true);
}
[IteratorStateMachine(typeof(<End>d__31))]
public IEnumerator End()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <End>d__31(0)
{
<>4__this = this
};
}
public void BlockingEnd()
{
if (tcpClient != null)
{
disconnectCalled = true;
ThreadsRunning = false;
readThread?.Join();
writeThread?.Join();
tcpClient.Close();
}
}
private void UpdateRateLimits()
{
if (ClientUserTags.HasBadge("broadcaster") || ClientUserTags.HasBadge("moderator"))
{
lock (rateLimitLock)
{
rateLimit = RateLimit.ChatModerator;
return;
}
}
lock (rateLimitLock)
{
rateLimit = RateLimit.ChatRegular;
}
}
private void ReadThreadLoop()
{
if (showThreadDebug)
{
Debug.Log((object)(Tags.thread + " Read thread started"));
}
using (NetworkStream networkStream = tcpClient.GetStream())
{
byte[] array = new byte[readBufferSize];
StringBuilder stringBuilder = new StringBuilder();
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] array2 = new char[readBufferSize + Mathf.Clamp(readBufferSize / 4, 1, 32)];
while (ThreadsRunning)
{
if (!CheckConnection(tcpClient.Client))
{
alertQueue.Enqueue(IRCReply.CONNECTION_INTERRUPTED);
return;
}
while (networkStream.DataAvailable)
{
int byteCount = networkStream.Read(array, 0, readBufferSize);
int chars = decoder.GetChars(array, 0, byteCount, array2, 0);
for (int i = 0; i < chars; i++)
{
if (array2[i] == '\n' || array2[i] == '\r')
{
if (stringBuilder.Length > 0)
{
HandleRawLine(stringBuilder.ToString());
stringBuilder.Clear();
}
}
else
{
stringBuilder.Append(array2[i]);
}
}
}
Thread.Sleep(readInterval);
}
}
if (showThreadDebug)
{
Debug.Log((object)(Tags.thread + " Read thread stopped"));
}
}
private bool CheckConnection(Socket socket)
{
bool flag = socket.Poll(1000, SelectMode.SelectRead);
bool flag2 = socket.Available == 0;
if ((flag && flag2) || !socket.Connected)
{
return false;
}
return true;
}
private void HandleRawLine(string raw)
{
if (showIRCDebug)
{
Debug.Log((object)(Tags.read + " " + raw));
}
string text = raw;
string tagString = string.Empty;
if (raw[0] == '@')
{
int num = raw.IndexOf(' ');
tagString = raw.Substring(0, num);
text = raw.Substring(num).TrimStart();
}
if (text[0] == ':')
{
string text2 = text.Substring(text.IndexOf(' ')).TrimStart();
text2 = text2.Substring(0, text2.IndexOf(' '));
switch (text2)
{
case "PRIVMSG":
HandlePRIVMSG(text, tagString);
break;
case "USERSTATE":
HandleUSERSTATE(text, tagString);
break;
case "NOTICE":
HandleNOTICE(text, tagString);
break;
case "353":
case "001":
HandleRPL(text2);
break;
}
}
if (raw.StartsWith("PING"))
{
Pong();
}
if (raw.StartsWith(":tmi.twitch.tv PONG"))
{
alertQueue.Enqueue(IRCReply.PONG_RECEIVED);
}
}
private void HandlePRIVMSG(string ircString, string tagString)
{
string login = ParseHelper.ParseLoginName(ircString);
string text = ParseHelper.ParseChannel(ircString);
string message = ParseHelper.ParseMessage(ircString);
IRCTags iRCTags = ParseHelper.ParseTags(tagString);
if (iRCTags.colorHex.Length <= 0)
{
iRCTags.colorHex = (useRandomColorForUndefined ? ChatColors.GetRandomNameColor(sessionRandom, login) : "#FFFFFF");
}
if (iRCTags.emotes.Length != 0)
{
Array.Sort(iRCTags.emotes, (ChatterEmote a, ChatterEmote b) => a.indexes[0].startIndex.CompareTo(b.indexes[0].startIndex));
}
chatterQueue.Enqueue(new Chatter(login, text, message, iRCTags));
}
private void HandleUSERSTATE(string ircString, string tagString)
{
IRCTags clientUserTags = ParseHelper.ParseTags(tagString);
ClientUserTags = clientUserTags;
UpdateRateLimits();
}
private void HandleNOTICE(string ircString, string tagString)
{
if (ircString.Contains(":Login authentication failed"))
{
alertQueue.Enqueue(IRCReply.BAD_LOGIN);
}
}
private void HandleRPL(string type)
{
if (!(type == "001"))
{
if (type == "353")
{
alertQueue.Enqueue(IRCReply.JOINED_CHANNEL);
}
return;
}
alertQueue.Enqueue(IRCReply.CONNECTED_TO_SERVER);
if (joinChannelOnStart)
{
IRC.Instance.JoinChannel(channel);
}
}
private void WriteThreadLoop()
{
if (showThreadDebug)
{
Debug.Log((object)(Tags.thread + " Write thread started"));
}
NetworkStream stream = tcpClient.GetStream();
while (ThreadsRunning)
{
while (!priorityWriteQueue.IsEmpty)
{
if (priorityWriteQueue.TryDequeue(out string result))
{
stream.WriteLine(result, showIRCDebug);
}
}
lock (rateLimitLock)
{
RateLimit rateLimit = this.rateLimit;
}
DateTime dateTime = DateTime.Now - this.rateLimit.timeSpan;
DateTime result2;
while (writeTimestamps.TryPeek(out result2) && result2 < dateTime)
{
writeTimestamps.TryDequeue(out var _);
}
while (!writeQueue.IsEmpty && writeTimestamps.Count < this.rateLimit.count)
{
if (writeQueue.TryDequeue(out string result4))
{
stream.WriteLine(result4, showIRCDebug);
writeTimestamps.Enqueue(DateTime.Now);
}
}
Thread.Sleep(writeInterval);
}
if (showThreadDebug)
{
Debug.Log((object)(Tags.thread + " Write thread stopped"));
}
}
public void Ping()
{
SendCommand("PING :tmi.twitch.tv", priority: true);
}
public void Pong()
{
SendCommand("PONG :tmi.twitch.tv", priority: true);
}
public void SendCommand(string command, bool priority = false)
{
if (priority)
{
priorityWriteQueue.Enqueue(command);
}
else
{
writeQueue.Enqueue(command);
}
}
public void SendChatMessage(string message)
{
if (message.Length <= 0)
{
Debug.LogWarning((object)(Tags.write + " Tried sending an empty chat message"));
}
else
{
SendCommand("PRIVMSG #" + channel + " :" + message);
}
}
}
public enum IRCReply
{
CONNECTED_TO_SERVER = 1,
PONG_RECEIVED = 7,
JOINED_CHANNEL = 353,
MISSING_LOGIN_INFO = 431,
BAD_LOGIN = 464,
CONNECTION_INTERRUPTED = 498,
NO_CONNECTION = 499
}
public enum ReadBufferSize
{
_32 = 0x20,
_64 = 0x40,
_128 = 0x80,
_256 = 0x100,
_512 = 0x200,
_1024 = 0x400,
_2048 = 0x800,
_4096 = 0x1000
}
public static class Tags
{
public static string read = "<color=#00ff00><b>[IRC READ]</b></color>";
public static string write = "<color=#5274ff><b>[IRC WRITE]</b></color>";
public static string thread = "<color=#ff5252><b>[THREAD]</b></color>";
public static string alert = "<color=#ffae00><b>[ALERT]</b></color>";
}
public static class ChatColors
{
public static string[] defaultNameColors = new string[15]
{
"#FF0000", "#00FF00", "#0000FF", "#B22222", "#FF7F50", "#9ACD32", "#FF4500", "#2E8B57", "#DAA520", "#D2691E",
"#5F9EA0", "#1E90FF", "#FF69B4", "#8A2BE2", "#00FF7F"
};
public static float grayscaleLow = 0.3f;
public static float grayscaleHigh = 1f;
public static string GetRandomNameColor(int sessionRandom, string login)
{
int num = sessionRandom + login[0] + login[login.Length - 1];
return defaultNameColors[num % defaultNameColors.Length];
}
public static Color NormalizeColor(Color color)
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
if (((Color)(ref color)).grayscale < grayscaleLow)
{
float num = grayscaleLow - ((Color)(ref color)).grayscale;
return new Color(color.r + num, color.g + num, color.b + num);
}
if (((Color)(ref color)).grayscale > grayscaleHigh)
{
float num2 = grayscaleHigh - ((Color)(ref color)).grayscale;
return new Color(color.r + num2, color.g + num2, color.b + num2);
}
return color;
}
}
}
namespace SBG_Chat_Integration
{
public class ChatAction
{
public string name { get; set; }
public Action onChoose { get; set; }
}
public class ChatAPI : MonoBehaviour
{
[CompilerGenerated]
private sealed class <VotingRoutine>d__11 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public ChatAPI <>4__this;
private float <showTime>5__2;
private float <timer>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <VotingRoutine>d__11(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Expected O, but got Unknown
int num = <>1__state;
ChatAPI chatAPI = <>4__this;
switch (num)
{
default:
return false;
case 0:
{
<>1__state = -1;
votes.Clear();
currentOptions.Clear();
countingVotes = false;
chatAPI.chattersThatHaveVoted.Clear();
int num3 = Random.Range(Plugin.minWaitTime.Value, Plugin.maxWaitTime.Value + 1);
Plugin.Log.LogInfo((object)$"Next vote in {num3} seconds.");
<>2__current = (object)new WaitForSeconds((float)num3);
<>1__state = 1;
return true;
}
case 1:
{
<>1__state = -1;
int count = Mathf.Min(Plugin.optionsToShow.Value, allActions.Count);
List<ChatAction> list = allActions.OrderBy((ChatAction x) => Random.value).Take(count).ToList();
Plugin.Log.LogInfo((object)"--- UPCOMING OPTIONS (Get Ready!) ---");
for (int i = 0; i < list.Count; i++)
{
int num2 = i + 1;
currentOptions.Add(num2, list[i]);
votes.Add(num2, 0);
Plugin.Log.LogInfo((object)$"[{num2}] {list[i].name}");
}
GUIManager.CreateOptionsGUI(list);
<showTime>5__2 = Plugin.showTimeBeforeVoting.Value;
Plugin.Log.LogInfo((object)$"Voting starts in {<showTime>5__2} seconds...");
goto IL_01d8;
}
case 2:
<>1__state = -1;
goto IL_01d8;
case 3:
{
<>1__state = -1;
break;
}
IL_01d8:
if (<showTime>5__2 > 0f)
{
GUIManager.UpdateTimer(<showTime>5__2);
<showTime>5__2 -= Time.deltaTime;
<>2__current = null;
<>1__state = 2;
return true;
}
GUIManager.StartVotingPhase();
countingVotes = true;
<timer>5__3 = Plugin.votingTime.Value;
Plugin.Log.LogInfo((object)"VOTING OPEN! Type the number in chat!");
break;
}
if (<timer>5__3 > 0f)
{
GUIManager.UpdateTimer(<timer>5__3);
<timer>5__3 -= Time.deltaTime;
<>2__current = null;
<>1__state = 3;
return true;
}
countingVotes = false;
chatAPI.ProcessWinner();
chatAPI.StartRandomSelection();
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public static ChatAPI instance;
private static List<ChatAction> allActions = new List<ChatAction>();
public static Dictionary<int, int> votes = new Dictionary<int, int>();
public static Dictionary<int, ChatAction> currentOptions = new Dictionary<int, ChatAction>();
public List<string> chattersThatHaveVoted = new List<string>();
public static bool countingVotes = false;
private Coroutine currentVoteCoroutine;
private void Awake()
{
instance = this;
}
public void RegisterNewAction(ChatAction chatAction)
{
if (string.IsNullOrEmpty(chatAction.name))
{
Plugin.Log.LogError((object)"The ChatAction must have a name!");
}
else if (chatAction.onChoose == null)
{
Plugin.Log.LogError((object)("ChatAction: " + chatAction.name + " must have an onChoose event!"));
}
else if (!allActions.Contains(chatAction))
{
allActions.Add(chatAction);
}
}
public void ForceCancelSelection()
{
((MonoBehaviour)this).StopAllCoroutines();
votes.Clear();
currentOptions.Clear();
countingVotes = false;
chattersThatHaveVoted.Clear();
GUIManager.CancelGUI();
}
public void StartRandomSelection()
{
if (allActions.Count == 0)
{
Plugin.Log.LogError((object)"Cannot start voting: No actions registered!");
return;
}
if (currentVoteCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(currentVoteCoroutine);
}
currentVoteCoroutine = ((MonoBehaviour)this).StartCoroutine(VotingRoutine());
}
[IteratorStateMachine(typeof(<VotingRoutine>d__11))]
private IEnumerator VotingRoutine()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <VotingRoutine>d__11(0)
{
<>4__this = this
};
}
private void ProcessWinner()
{
if (votes.Count == 0)
{
return;
}
int key = votes.Aggregate((KeyValuePair<int, int> l, KeyValuePair<int, int> r) => (l.Value <= r.Value) ? r : l).Key;
int num = votes[key];
ChatAction chatAction = currentOptions[key];
Plugin.Log.LogInfo((object)$"--- WINNER: {chatAction.name} with {num} votes! ---");
GUIManager.ShowWinner(key);
try
{
chatAction.onChoose?.Invoke();
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Error executing ChatAction " + chatAction.name + ": " + ex.Message));
}
}
public void AddVote(int vote)
{
if (countingVotes && currentOptions.ContainsKey(vote))
{
votes[vote]++;
GUIManager.UpdateGUI(vote, votes[vote]);
}
}
}
[BepInPlugin("com.atomic.sbgchatintegration", "SBG_Chat_Integration", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
private TwitchConnection connection;
public static Harmony harmony;
public static ConfigEntry<int> showTimeBeforeVoting;
public static ConfigEntry<int> votingTime;
public static ConfigEntry<int> optionsToShow;
public static ConfigEntry<int> minWaitTime;
public static ConfigEntry<int> maxWaitTime;
public static ConfigEntry<bool> peopleVoteMultipleTimes;
public static ConfigEntry<string> guiPosition;
public static ConfigEntry<string> twitchUsername;
public static bool gotUIRefs;
public const string Id = "com.atomic.sbgchatintegration";
internal static ManualLogSource Log { get; private set; }
public static string Name => "SBG_Chat_Integration";
public static string Version => "0.1.0";
private void Awake()
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Expected O, but got Unknown
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Expected O, but got Unknown
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Expected O, but got Unknown
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Expected O, but got Unknown
//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
//IL_01c5: Expected O, but got Unknown
//IL_0208: Unknown result type (might be due to invalid IL or missing references)
//IL_0212: Expected O, but got Unknown
//IL_0221: Unknown result type (might be due to invalid IL or missing references)
//IL_0227: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
twitchUsername = ((BaseUnityPlugin)this).Config.Bind<string>("Setup", "Twitch Username", "your_twitch_handle", "The username that the mod will read chat from.");
showTimeBeforeVoting = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Show Time Before Voting", 5, new ConfigDescription("(In Seconds): How long the options will wait on screen before stating to vote. Consider stream delay.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 20), Array.Empty<object>()));
votingTime = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Voting Time", 10, new ConfigDescription("(In Seconds): How long the mod will be tracking votes for.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 30), Array.Empty<object>()));
optionsToShow = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Options to show", 3, new ConfigDescription("How many options can be picked at once", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 5), Array.Empty<object>()));
minWaitTime = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Minimum Intermission", 30, new ConfigDescription("The minimum time to wait after an event", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 300), Array.Empty<object>()));
maxWaitTime = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Maximum Intermission", 60, new ConfigDescription("The maximum time to wait after an event", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 300), Array.Empty<object>()));
peopleVoteMultipleTimes = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Can Vote Multiple Times", false, "If True people can vote for the same poll as many times as they want");
guiPosition = ((BaseUnityPlugin)this).Config.Bind<string>("General", "GUI Position", "Top-Right", new ConfigDescription("The position the voting UI will be in.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[8] { "Top-Right", "Top-Middle", "Top-Left", "Middle-Left", "Middle-Right", "Bottom-Left", "Bottom-Middle", "Bottom-Right" }), Array.Empty<object>()));
if (maxWaitTime.Value < minWaitTime.Value)
{
Log.LogWarning((object)"Max Wait Time was lower than Min. Adjusting Max to match Min.");
maxWaitTime.Value = minWaitTime.Value;
}
harmony = new Harmony("com.atomic.sbgchatintegration");
harmony.PatchAll();
GameObject val = new GameObject("ChatManager");
((Object)val).hideFlags = (HideFlags)61;
ChatAPI chatAPI = val.AddComponent<ChatAPI>();
GUIManager gUIManager = val.AddComponent<GUIManager>();
if (twitchUsername.Value != "your_twitch_handle")
{
IRC iRC = val.AddComponent<IRC>();
iRC.showIRCDebug = false;
iRC.username = "justinfan12345";
iRC.oauth = "pass";
iRC.channel = twitchUsername.Value;
IRC.Instance.OnChatMessage += OnTwitchChatMessage;
IRC.Instance.Connect();
Log.LogInfo((object)("Attempting anonymous connect to " + iRC.channel + "..."));
}
Object.DontDestroyOnLoad((Object)(object)val);
DefaultActions.RegisterActions();
}
private void OnTwitchChatMessage(Chatter chatter)
{
Log.LogInfo((object)(chatter.login + ": " + chatter.message));
if (ChatAPI.countingVotes && (!ChatAPI.instance.chattersThatHaveVoted.Contains(chatter.login) || peopleVoteMultipleTimes.Value) && int.TryParse(chatter.message, out var result))
{
ChatAPI.instance.AddVote(result);
if (!peopleVoteMultipleTimes.Value)
{
ChatAPI.instance.chattersThatHaveVoted.Add(chatter.login);
}
}
}
private void OnDisable()
{
IRC.Instance.Disconnect();
}
}
[HarmonyPatch]
public static class SBG_ChatIntegration_Patches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(GameManager), "RegisterPlayer")]
public static void StartVoting(GameManager __instance, PlayerInfo playerInfo)
{
if (((NetworkBehaviour)playerInfo).isLocalPlayer)
{
Plugin.Log.LogInfo((object)"Passed checks...");
ChatAPI.instance.StartRandomSelection();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(MainMenu), "Awake")]
public static void StopVotes(MainMenu __instance)
{
ChatAPI.instance.ForceCancelSelection();
}
}
}
namespace SBG_Chat_Integration.UI
{
public class GUIManager : MonoBehaviour
{
[CompilerGenerated]
private sealed class <FadeOutSequence>d__45 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float delay;
public GUIManager <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <FadeOutSequence>d__45(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
int num = <>1__state;
GUIManager gUIManager = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(delay);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
gUIManager.isFadingOut = true;
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static GUIManager instance;
private static List<ChatAction> currentActions = new List<ChatAction>();
private static Dictionary<int, int> currentVotes = new Dictionary<int, int>();
private static float timerValue;
private static bool isVotingOpen = false;
private static bool isPreVoting = false;
private static bool isShowingWinner = false;
private static float totalVotes = 0f;
private static int winningOption = -1;
private GUIStyle boxStyle;
private GUIStyle titleStyle;
private GUIStyle optionStyle;
private GUIStyle timerStyle;
private GUIStyle voteBarStyle;
private GUIStyle percentageStyle;
private GUIStyle winnerStyle;
private Dictionary<int, float> targetPercentages = new Dictionary<int, float>();
private Dictionary<int, float> currentPercentages = new Dictionary<int, float>();
private Dictionary<int, Color> targetBarColors = new Dictionary<int, Color>();
private Dictionary<int, Color> currentBarColors = new Dictionary<int, Color>();
private float animationSpeed = 5f;
private float fadeAlpha = 1f;
private float fadeSpeed = 2f;
private bool isFadingOut;
private const float windowWidth = 320f;
private float windowHeight;
private Rect windowRect;
private static string currentPosition = "Top-Right";
private Texture2D backgroundTexture;
private Texture2D barBackgroundTexture;
private Dictionary<Color, Texture2D> colorTextures = new Dictionary<Color, Texture2D>();
private void Awake()
{
instance = this;
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
}
private void Start()
{
UpdateWindowPosition(Plugin.guiPosition.Value);
Plugin.guiPosition.SettingChanged += delegate
{
UpdateWindowPosition(Plugin.guiPosition.Value);
};
}
private void Update()
{
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
if (isVotingOpen || isShowingWinner)
{
for (int i = 1; i <= currentActions.Count; i++)
{
if (!currentPercentages.ContainsKey(i))
{
currentPercentages[i] = 0f;
}
float num = (targetPercentages.ContainsKey(i) ? targetPercentages[i] : 0f);
currentPercentages[i] = Mathf.Lerp(currentPercentages[i], num, Time.deltaTime * animationSpeed);
if (!currentBarColors.ContainsKey(i))
{
currentBarColors[i] = Color.red;
}
Color val = (targetBarColors.ContainsKey(i) ? targetBarColors[i] : Color.red);
currentBarColors[i] = Color.Lerp(currentBarColors[i], val, Time.deltaTime * animationSpeed);
}
}
if (isFadingOut)
{
fadeAlpha = Mathf.MoveTowards(fadeAlpha, 0f, Time.deltaTime * fadeSpeed);
if (fadeAlpha <= 0.001f)
{
fadeAlpha = 0f;
isFadingOut = false;
currentActions.Clear();
currentVotes.Clear();
}
}
}
private void UpdateWindowPosition(string position)
{
//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
currentPosition = position;
windowHeight = CalculateWindowHeight();
float num = 20f;
float num2 = 20f;
switch (position)
{
case "Top-Middle":
num = ((float)Screen.width - 320f) / 2f;
break;
case "Top-Right":
num = (float)Screen.width - 320f - 20f;
break;
case "Middle-Left":
num2 = ((float)Screen.height - windowHeight) / 2f;
break;
case "Middle-Right":
num = (float)Screen.width - 320f - 20f;
num2 = ((float)Screen.height - windowHeight) / 2f;
break;
case "Bottom-Left":
num2 = (float)Screen.height - windowHeight - 20f;
break;
case "Bottom-Middle":
num = ((float)Screen.width - 320f) / 2f;
num2 = (float)Screen.height - windowHeight - 20f;
break;
case "Bottom-Right":
num = (float)Screen.width - 320f - 20f;
num2 = (float)Screen.height - windowHeight - 20f;
break;
}
windowRect = new Rect(num, num2, 320f, windowHeight);
}
private float CalculateWindowHeight()
{
if (currentActions.Count == 0)
{
return 0f;
}
float num = 85f;
float num2 = (isVotingOpen ? 30f : 0f);
float num3 = 78f;
return num + num2 + num3 * (float)currentActions.Count + 20f;
}
private Texture2D GetColorTexture(Color color)
{
//IL_0002: 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_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
Color val = default(Color);
((Color)(ref val))..ctor(Mathf.Round(color.r * 100f) / 100f, Mathf.Round(color.g * 100f) / 100f, Mathf.Round(color.b * 100f) / 100f, color.a);
if (colorTextures.TryGetValue(val, out Texture2D value))
{
return value;
}
Texture2D val2 = new Texture2D(1, 1);
val2.SetPixel(0, 0, val);
val2.Apply();
colorTextures[val] = val2;
return val2;
}
private void DrawTextWithOutline(Rect rect, string text, GUIStyle style, Color outColor, Color inColor, float thickness = 1f)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: 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_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: 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)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_0127: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
GUIStyle val = new GUIStyle(style);
val.normal.textColor = outColor;
Color color = GUI.color;
GUI.color = new Color(outColor.r, outColor.g, outColor.b, outColor.a * color.a);
Rect val2 = rect;
((Rect)(ref val2)).x = ((Rect)(ref val2)).x - thickness;
GUI.Label(val2, text, val);
((Rect)(ref val2)).x = ((Rect)(ref val2)).x + thickness * 2f;
GUI.Label(val2, text, val);
((Rect)(ref val2)).x = ((Rect)(ref val2)).x - thickness;
((Rect)(ref val2)).y = ((Rect)(ref val2)).y - thickness;
GUI.Label(val2, text, val);
((Rect)(ref val2)).y = ((Rect)(ref val2)).y + thickness * 2f;
GUI.Label(val2, text, val);
((Rect)(ref val2)).x = ((Rect)(ref val2)).x - thickness;
((Rect)(ref val2)).y = ((Rect)(ref val2)).y - thickness * 2f;
GUI.Label(val2, text, val);
((Rect)(ref val2)).x = ((Rect)(ref val2)).x + thickness * 2f;
GUI.Label(val2, text, val);
((Rect)(ref val2)).y = ((Rect)(ref val2)).y + thickness * 2f;
GUI.Label(val2, text, val);
((Rect)(ref val2)).x = ((Rect)(ref val2)).x - thickness * 2f;
GUI.Label(val2, text, val);
GUI.color = color;
style.normal.textColor = inColor;
GUI.Label(rect, text, style);
}
private void InitializeStyles()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Expected O, but got Unknown
//IL_008c: Expected O, but got Unknown
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Expected O, but got Unknown
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Expected O, but got Unknown
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Expected O, but got Unknown
//IL_0136: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_0156: Expected O, but got Unknown
//IL_0161: Unknown result type (might be due to invalid IL or missing references)
//IL_0166: Unknown result type (might be due to invalid IL or missing references)
//IL_017c: Expected O, but got Unknown
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_019b: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
//IL_01b7: Expected O, but got Unknown
if (boxStyle == null)
{
backgroundTexture = GetColorTexture(new Color(0.05f, 0.05f, 0.05f, 0.9f));
barBackgroundTexture = GetColorTexture(new Color(0.2f, 0.2f, 0.2f, 0.5f));
GUIStyle val = new GUIStyle(GUI.skin.box);
val.normal.background = backgroundTexture;
val.padding = new RectOffset(10, 10, 10, 10);
boxStyle = val;
GUIStyle val2 = new GUIStyle(GUI.skin.label)
{
fontSize = 20,
fontStyle = (FontStyle)1,
alignment = (TextAnchor)4
};
val2.normal.textColor = new Color(1f, 0.84f, 0f);
titleStyle = val2;
GUIStyle val3 = new GUIStyle(GUI.skin.label)
{
fontSize = 16
};
val3.normal.textColor = Color.white;
optionStyle = val3;
GUIStyle val4 = new GUIStyle(optionStyle)
{
fontStyle = (FontStyle)1
};
val4.normal.textColor = Color.green;
winnerStyle = val4;
timerStyle = new GUIStyle(GUI.skin.label)
{
fontSize = 18,
fontStyle = (FontStyle)1,
alignment = (TextAnchor)4
};
GUIStyle val5 = new GUIStyle(GUI.skin.box);
val5.normal.background = barBackgroundTexture;
voteBarStyle = val5;
GUIStyle val6 = new GUIStyle(GUI.skin.label)
{
fontSize = 12,
fontStyle = (FontStyle)1,
alignment = (TextAnchor)4
};
val6.normal.textColor = Color.white;
percentageStyle = val6;
}
}
private void OnGUI()
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
//IL_03f7: Expected O, but got Unknown
//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0205: Unknown result type (might be due to invalid IL or missing references)
//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
//IL_025e: Unknown result type (might be due to invalid IL or missing references)
//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
//IL_02df: Unknown result type (might be due to invalid IL or missing references)
//IL_0326: Unknown result type (might be due to invalid IL or missing references)
//IL_032b: Unknown result type (might be due to invalid IL or missing references)
//IL_0330: Unknown result type (might be due to invalid IL or missing references)
//IL_0349: Unknown result type (might be due to invalid IL or missing references)
//IL_034f: Unknown result type (might be due to invalid IL or missing references)
//IL_0363: Unknown result type (might be due to invalid IL or missing references)
//IL_037c: Unknown result type (might be due to invalid IL or missing references)
//IL_0381: Unknown result type (might be due to invalid IL or missing references)
InitializeStyles();
if (currentActions.Count == 0 || fadeAlpha <= 0.01f)
{
return;
}
windowHeight = CalculateWindowHeight();
UpdateWindowPosition(currentPosition);
GUI.color = new Color(1f, 1f, 1f, fadeAlpha);
GUI.Box(windowRect, "", boxStyle);
GUILayout.BeginArea(new Rect(((Rect)(ref windowRect)).x + 10f, ((Rect)(ref windowRect)).y + 10f, ((Rect)(ref windowRect)).width - 20f, ((Rect)(ref windowRect)).height - 20f));
GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
string text = (isShowingWinner ? "Winner!" : (isPreVoting ? "Upcoming Vote" : "Vote Now!"));
GUILayout.Label(text, titleStyle, Array.Empty<GUILayoutOption>());
if (!isShowingWinner)
{
timerStyle.normal.textColor = (isPreVoting ? Color.gray : Color.green);
GUILayout.Label(isPreVoting ? $"Starts in: {Mathf.CeilToInt(timerValue)}s" : $"{Mathf.CeilToInt(timerValue)}s remaining", timerStyle, Array.Empty<GUILayoutOption>());
}
else
{
GUILayout.Space(10f);
}
for (int i = 0; i < currentActions.Count; i++)
{
int num = i + 1;
bool flag = isShowingWinner && num == winningOption;
int valueOrDefault = currentVotes.GetValueOrDefault(num, 0);
float num2 = ((totalVotes > 0f) ? ((float)valueOrDefault / totalVotes * 100f) : 0f);
targetPercentages[num] = num2;
if (flag)
{
targetBarColors[num] = Color.green;
}
else
{
targetBarColors[num] = Color.Lerp(Color.red, Color.cyan, num2 / 100f);
}
GUILayout.BeginVertical(boxStyle, Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUIStyle val = (flag ? winnerStyle : optionStyle);
if (isPreVoting)
{
val.normal.textColor = new Color(1f, 1f, 1f, 0.5f);
}
GUILayout.Label($"[{num}] {currentActions[i].name}", val, Array.Empty<GUILayoutOption>());
if (!isPreVoting)
{
GUILayout.FlexibleSpace();
GUILayout.Label(valueOrDefault.ToString(), val, Array.Empty<GUILayoutOption>());
}
GUILayout.EndHorizontal();
GUILayout.Space(5f);
Rect rect = GUILayoutUtility.GetRect(((Rect)(ref windowRect)).width - 40f, 20f);
GUI.Box(rect, "", voteBarStyle);
float valueOrDefault2 = currentPercentages.GetValueOrDefault(num, 0f);
if (valueOrDefault2 > 0f)
{
float num3 = valueOrDefault2 / 100f * ((Rect)(ref rect)).width;
Color valueOrDefault3 = currentBarColors.GetValueOrDefault(num, Color.red);
GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, num3, ((Rect)(ref rect)).height), (Texture)(object)GetColorTexture(valueOrDefault3));
}
if (!isPreVoting)
{
DrawTextWithOutline(rect, $"{num2:F0}%", percentageStyle, Color.black, Color.white);
}
GUILayout.EndVertical();
}
if (isVotingOpen)
{
GUILayout.Space(5f);
GUIStyle val2 = new GUIStyle(GUI.skin.label)
{
alignment = (TextAnchor)4,
fontSize = 12
};
val2.normal.textColor = Color.gray;
GUILayout.Label("Type the number in chat", val2, Array.Empty<GUILayoutOption>());
}
GUILayout.EndVertical();
GUILayout.EndArea();
}
public static void CreateOptionsGUI(List<ChatAction> actions)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)instance == (Object)null)
{
instance = new GameObject("VoteGUI").AddComponent<GUIManager>();
}
currentActions = actions;
currentVotes.Clear();
instance.targetPercentages.Clear();
instance.currentPercentages.Clear();
instance.targetBarColors.Clear();
instance.currentBarColors.Clear();
for (int i = 1; i <= actions.Count; i++)
{
currentVotes[i] = 0;
instance.currentPercentages[i] = 0f;
instance.targetPercentages[i] = 0f;
instance.currentBarColors[i] = Color.red;
instance.targetBarColors[i] = Color.red;
}
isPreVoting = true;
isVotingOpen = false;
isShowingWinner = false;
instance.fadeAlpha = 1f;
instance.isFadingOut = false;
timerValue = Plugin.showTimeBeforeVoting.Value;
totalVotes = 0f;
winningOption = -1;
}
public static void UpdateGUI(int target, int count)
{
if (currentVotes.ContainsKey(target))
{
currentVotes[target] = count;
totalVotes = currentVotes.Values.Sum();
}
}
public static void CancelGUI()
{
if (!((Object)(object)instance == (Object)null))
{
instance.isFadingOut = true;
}
}
public static void StartVotingPhase()
{
isPreVoting = false;
isVotingOpen = true;
timerValue = Plugin.votingTime.Value;
}
public static void ShowWinner(int num)
{
winningOption = num;
isShowingWinner = true;
isVotingOpen = false;
((MonoBehaviour)instance).StartCoroutine(instance.FadeOutSequence(3f));
}
[IteratorStateMachine(typeof(<FadeOutSequence>d__45))]
private IEnumerator FadeOutSequence(float delay)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <FadeOutSequence>d__45(0)
{
<>4__this = this,
delay = delay
};
}
public static void UpdateTimer(float time)
{
timerValue = time;
}
}
}
namespace SBG_Chat_Integration.Actions
{
[HarmonyPatch]
public static class ActionPatches
{
public static bool canMove = true;
public static bool invertedControls = false;
public static bool superSlow = false;
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerGolfer), "CanMove")]
public static bool OverrideMove(PlayerGolfer __instance, ref bool __result)
{
if (((NetworkBehaviour)__instance).isLocalPlayer)
{
if (canMove)
{
return true;
}
__result = false;
return false;
}
return true;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerMovement), "ApplyMovement")]
public static void InvertMovementPrefix(PlayerMovement __instance)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
if (((NetworkBehaviour)__instance).isLocalPlayer && invertedControls)
{
__instance.worldMoveVector3d *= -1f;
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(GolfCartInfo), "ServerEnter")]
public static bool RestrictAccess(GolfCartInfo __instance, PlayerInfo newPassenger)
{
if (((NetworkBehaviour)newPassenger).isLocalPlayer)
{
return canMove;
}
return true;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(PlayerMovement), "UpdateMovementSpeed")]
public static void ApplySuperSlow(PlayerMovement __instance)
{
if (((NetworkBehaviour)__instance).isLocalPlayer && superSlow)
{
__instance.speed *= 0.5f;
}
}
}
public class DefaultActions
{
[CompilerGenerated]
private sealed class <DanceSequence>d__1 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float duration;
private PlayerInfo <player>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DanceSequence>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<player>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
ActionPatches.canMove = false;
<player>5__2 = GameManager.LocalPlayerInfo;
<player>5__2.ExitGolfCart((GolfCartExitType)0);
<player>5__2.SetEmoteBeingPlayed((Emote)1);
GameManager.LocalPlayerInventory.InformIsPlayingEmoteChanged();
<>2__current = (object)new WaitForSeconds(duration);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
ActionPatches.canMove = true;
<player>5__2.SetEmoteBeingPlayed((Emote)0);
GameManager.LocalPlayerInventory.InformIsPlayingEmoteChanged();
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <InvertedControls>d__2 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float duration;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <InvertedControls>d__2(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
ActionPatches.invertedControls = true;
Plugin.Log.LogInfo((object)"Movement Inverted!");
<>2__current = (object)new WaitForSeconds(duration);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
ActionPatches.invertedControls = false;
Plugin.Log.LogInfo((object)"Movement Restored.");
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <SuperSlow>d__3 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float duration;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SuperSlow>d__3(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
ActionPatches.superSlow = true;
<>2__current = (object)new WaitForSeconds(duration);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
ActionPatches.superSlow = false;
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public static void RegisterActions()
{
ChatAction chatAction = new ChatAction
{
name = "Fling ball",
onChoose = delegate
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
float num5 = 100f;
float num6 = 10f;
float num7 = 1f;
Vector3 onUnitSphere = Random.onUnitSphere;
Vector3 val4 = ((Component)GameManager.LocalPlayerAsGolfer.ownBall.rigidbody).transform.position + Vector3.down * 0.5f;
GameManager.LocalPlayerAsGolfer.ownBall.rigidbody.AddExplosionForce(num5, val4, num6, num7, (ForceMode)1);
Plugin.Log.LogInfo((object)"Chat triggered ball fling!");
}
};
ChatAction chatAction2 = new ChatAction
{
name = "Do nothing",
onChoose = delegate
{
Plugin.Log.LogInfo((object)"You got lucky...");
}
};
ChatAction chatAction3 = new ChatAction
{
name = "Fling Player",
onChoose = delegate
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
Rigidbody rigidbody = GameManager.LocalPlayerMovement.rigidbody;
if (!((Object)(object)rigidbody == (Object)null))
{
float num2 = 9000f;
float num3 = 100f;
float num4 = 1f;
Vector3 val3 = ((Component)rigidbody).transform.position + Vector3.down * 0.5f;
rigidbody.AddExplosionForce(num2, val3, num3, num4, (ForceMode)1);
Plugin.Log.LogInfo((object)"BOOM! Player flung by chat.");
}
}
};
ChatAction chatAction4 = new ChatAction
{
name = "Respawn Player",
onChoose = delegate
{
GameManager.LocalPlayerMovement.TryBeginRespawn(false);
}
};
ChatAction chatAction5 = new ChatAction
{
name = "Spawn 10 stacked golf carts",
onChoose = delegate
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
if (NetworkServer.active)
{
PlayerInfo playerInfo = GameManager.LocalPlayerAsGolfer.PlayerInfo;
GolfCartInfo prefab = GameManager.GolfCartSettings.Prefab;
for (int j = 0; j < 10; j++)
{
float num = (float)j * 2.5f;
Vector3 val = ((Component)playerInfo).transform.position + new Vector3(0f, num, 0f);
GolfCartInfo val2 = Object.Instantiate<GolfCartInfo>(prefab, val, Quaternion.Euler(0f, ((Component)playerInfo).transform.eulerAngles.y, 0f));
if (j == 0)
{
val2.ServerReserveDriverSeatPreNetworkSpawn(playerInfo);
NetworkServer.Spawn(((Component)val2).gameObject, (NetworkConnectionToClient)null);
val2.ServerReserveDriverSeatPostNetworkSpawn();
val2.ServerEnter(playerInfo);
Plugin.Log.LogInfo((object)"Spawned base cart and seated player.");
}
else
{
NetworkServer.Spawn(((Component)val2).gameObject, (NetworkConnectionToClient)null);
Plugin.Log.LogInfo((object)$"Stacked cart {j + 1}/10 at height {val.y}");
}
}
}
}
};
ChatAction chatAction6 = new ChatAction
{
name = "Reset Ball",
onChoose = delegate
{
GameManager.LocalPlayerAsGolfer.CmdRestartBall();
}
};
ChatAction chatAction7 = new ChatAction
{
name = "Give 5 Strokes",
onChoose = delegate
{
for (int i = 0; i < 5; i++)
{
CourseManager.AddPenaltyStroke(GameManager.LocalPlayerAsGolfer, false);
}
}
};
ChatAction chatAction8 = new ChatAction
{
name = "Dance for 5 seconds",
onChoose = delegate
{
((MonoBehaviour)ChatAPI.instance).StartCoroutine(DanceSequence(5f));
}
};
ChatAction chatAction9 = new ChatAction
{
name = "10s of inverted movements",
onChoose = delegate
{
((MonoBehaviour)ChatAPI.instance).StartCoroutine(InvertedControls(10f));
}
};
ChatAction chatAction10 = new ChatAction
{
name = "2x Slower for 10 seconds",
onChoose = delegate
{
((MonoBehaviour)ChatAPI.instance).StartCoroutine(SuperSlow(10f));
}
};
ChatAPI.instance.RegisterNewAction(chatAction);
ChatAPI.instance.RegisterNewAction(chatAction2);
ChatAPI.instance.RegisterNewAction(chatAction3);
ChatAPI.instance.RegisterNewAction(chatAction4);
ChatAPI.instance.RegisterNewAction(chatAction5);
ChatAPI.instance.RegisterNewAction(chatAction6);
ChatAPI.instance.RegisterNewAction(chatAction7);
ChatAPI.instance.RegisterNewAction(chatAction8);
ChatAPI.instance.RegisterNewAction(chatAction9);
ChatAPI.instance.RegisterNewAction(chatAction10);
}
[IteratorStateMachine(typeof(<DanceSequence>d__1))]
private static IEnumerator DanceSequence(float duration)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DanceSequence>d__1(0)
{
duration = duration
};
}
[IteratorStateMachine(typeof(<InvertedControls>d__2))]
private static IEnumerator InvertedControls(float duration)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <InvertedControls>d__2(0)
{
duration = duration
};
}
[IteratorStateMachine(typeof(<SuperSlow>d__3))]
private static IEnumerator SuperSlow(float duration)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SuperSlow>d__3(0)
{
duration = duration
};
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ConstantExpectedAttribute : Attribute
{
public object? Min { get; set; }
public object? Max { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ExperimentalAttribute : Attribute
{
public string DiagnosticId { get; }
public string? UrlFormat { get; set; }
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class StringSyntaxAttribute : Attribute
{
public const string CompositeFormat = "CompositeFormat";
public const string DateOnlyFormat = "DateOnlyFormat";
public const string DateTimeFormat = "DateTimeFormat";
public const string EnumFormat = "EnumFormat";
public const string GuidFormat = "GuidFormat";
public const string Json = "Json";
public const string NumericFormat = "NumericFormat";
public const string Regex = "Regex";
public const string TimeOnlyFormat = "TimeOnlyFormat";
public const string TimeSpanFormat = "TimeSpanFormat";
public const string Uri = "Uri";
public const string Xml = "Xml";
public string Syntax { get; }
public object?[] Arguments { get; }
public StringSyntaxAttribute(string syntax)
{
Syntax = syntax;
Arguments = new object[0];
}
public StringSyntaxAttribute(string syntax, params object?[] arguments)
{
Syntax = syntax;
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class UnscopedRefAttribute : Attribute
{
}
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresPreviewFeaturesAttribute : Attribute
{
public string? Message { get; }
public string? Url { get; set; }
public RequiresPreviewFeaturesAttribute()
{
}
public RequiresPreviewFeaturesAttribute(string? message)
{
Message = message;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public string ParameterName { get; }
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CollectionBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public string MethodName { get; }
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public const string RefStructs = "RefStructs";
public const string RequiredMembers = "RequiredMembers";
public string FeatureName { get; }
public bool IsOptional { get; set; }
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public string[] Arguments { get; }
public InterpolatedStringHandlerArgumentAttribute(string argument)
{
Arguments = new string[1] { argument };
}
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
{
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerAttribute : Attribute
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal static class IsExternalInit
{
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class OverloadResolutionPriorityAttribute : Attribute
{
public int Priority { get; }
public OverloadResolutionPriorityAttribute(int priority)
{
Priority = priority;
}
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
[ExcludeFromCodeCoverage]
internal sealed class ParamCollectionAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiredMemberAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresLocationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}