using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ServerDiceRoll.Patches;
using ServerDiceRoll.Support;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("bbar.Mods.ServerDiceRoll")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("Simple Server Dice Roller")]
[assembly: AssemblyTitle("bbar.Mods.ServerDiceRoll")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
namespace ServerDiceRoll
{
[BepInPlugin("bbar.Mods.ServerDiceRoll", "Simple Server Dice Roller", "1.0.1")]
public class VSDR : BaseUnityPlugin
{
private Harmony _harmony;
public static VSDR Instance { get; private set; }
public ManualLogSource Log { get; private set; }
public PluginConfig PluginConfig { get; private set; }
public void Awake()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
PluginConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
Assembly executingAssembly = Assembly.GetExecutingAssembly();
_harmony = new Harmony("bbar.Mods.ServerDiceRoll");
_harmony.PatchAll(executingAssembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin bbar.Mods.ServerDiceRoll is loaded!");
}
private void OnDestroy()
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
PluginConfig?.Dispose();
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "bbar.Mods.ServerDiceRoll";
public const string PLUGIN_NAME = "Simple Server Dice Roller";
public const string PLUGIN_VERSION = "1.0.1";
}
}
namespace ServerDiceRoll.Support
{
public class MessageHelper
{
private readonly MessageQueue _messages;
private readonly MessageType _location;
public MessageHelper()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
_messages = new MessageQueue(VSDR.Instance.PluginConfig.MessageQueueLength, TimeSpan.FromSeconds(VSDR.Instance.PluginConfig.MessageQueueTimeout).Ticks);
_location = VSDR.Instance.PluginConfig.MessageLocation;
}
public void ResendBroadcasts(long peerId)
{
_messages.PurgeOutdated(1);
MessagePeer(peerId, _messages.ConcatMessages());
}
public void MessagePeer(long peerId, string text)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected I4, but got Unknown
VSDR.Instance.Log.LogDebug((object)$"Messaging peer {peerId}: {text}");
ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "ShowMessage", new object[2]
{
(int)_location,
text
});
}
public void Broadcast(string text)
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
_messages.AddMessage(text);
string text2 = _messages.ConcatMessages();
VSDR.Instance.Log.LogDebug((object)("Messaging everyone: " + text2));
MessageHud.instance.MessageAll(_location, text2);
}
}
public class MessageQueue
{
protected struct MsgPair
{
public string Message;
public long TimeStamp;
public MsgPair(string message, long timeStamp)
{
Message = message;
TimeStamp = timeStamp;
}
}
private readonly ConcurrentQueue<MsgPair> _msgQueue = new ConcurrentQueue<MsgPair>();
public int Count => _msgQueue.Count;
public int Limit { get; }
public long Timeout { get; }
public MessageQueue(int limit, long timeout)
{
Limit = limit;
Timeout = timeout;
}
public void AddMessage(string message)
{
_msgQueue.Enqueue(new MsgPair(message, DateTime.Now.Ticks));
while (_msgQueue.Count > Limit)
{
_msgQueue.TryDequeue(out var _);
}
PurgeOutdated();
}
public void PurgeOutdated(int keep = 0)
{
if (Timeout >= 1)
{
long ticks = DateTime.Now.Ticks;
MsgPair result;
while (_msgQueue.Count > keep && _msgQueue.TryPeek(out result) && ticks - result.TimeStamp > Timeout)
{
_msgQueue.TryDequeue(out var _);
}
}
}
public string ConcatMessages()
{
return ToString();
}
public override string ToString()
{
MsgPair[] source = _msgQueue.ToArray();
return string.Join("\n", source.Select((MsgPair p) => p.Message));
}
}
public class PluginConfig : IDisposable
{
private readonly ConfigEntry<string> _cmdPrefix;
private readonly ConfigEntry<string> _cmdRoll;
private readonly ConfigEntry<string> _cmdAward;
private readonly ConfigEntry<string> _cmdViewResults;
private readonly ConfigEntry<int> _rollMax;
private readonly ConfigEntry<string> _colorUsage;
private readonly ConfigEntry<string> _colorRoll;
private readonly ConfigEntry<string> _colorAward;
private readonly ConfigEntry<string> _colorHighlight;
private readonly ConfigEntry<int> _msgQueueSize;
private readonly ConfigEntry<int> _msgQueueTimeoutSecs;
private readonly ConfigEntry<MessageType> _msgLocation;
public string CmdPrefix => _cmdPrefix.Value;
public string CmdRoll => _cmdRoll.Value;
public string CmdAward => _cmdAward.Value;
public string CmdResults => _cmdViewResults.Value;
public int RollMax => _rollMax.Value;
public string ColorUsage => _colorUsage.Value;
public string ColorRoll => _colorRoll.Value;
public string ColorAward => _colorAward.Value;
public string ColorHighlight => _colorHighlight.Value;
public int MessageQueueLength => _msgQueueSize.Value;
public int MessageQueueTimeout => _msgQueueTimeoutSecs.Value;
public MessageType MessageLocation => _msgLocation.Value;
public PluginConfig(ConfigFile config)
{
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Expected O, but got Unknown
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Expected O, but got Unknown
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_019e: Expected O, but got Unknown
//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
//IL_01d0: Expected O, but got Unknown
_cmdPrefix = config.Bind<string>("Commands", "CommandPrefix", "!", new ConfigDescription("Character to use to start a command with this plugin. Since this is a server-side plugin a slash cannot be used (as that indicates a Valheim command, handled client-side).", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "!", "@", "#", "$", "%" }), Array.Empty<object>()));
_cmdRoll = config.Bind<string>("Commands", "RollCommand", "roll", "String to use to trigger the 'roll' command.");
_cmdAward = config.Bind<string>("Commands", "AwardCommand", "award", "String to use to trigger the 'award' command.");
_cmdViewResults = config.Bind<string>("Commands", "CommandViewResults", "results", "String to use to trigger the 'results' command.");
_rollMax = config.Bind<int>("Commands", "DefaultRoll", 100, new ConfigDescription("The default value to use for the 'roll' command.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 100000), Array.Empty<object>()));
_colorUsage = config.Bind<string>("Color", "Usage", "red", "The color to use for the usage messages. (Messages sent to players when the enter a command incorrectly.) Unset this to use the Valheim default.");
_colorRoll = config.Bind<string>("Color", "Roll", "orange", "The color to use for the 'roll' messages. Unset this to use the Valheim default.");
_colorAward = config.Bind<string>("Color", "Award", "orange", "The color to use for the 'award' messages. Unset this to use the Valheim default.");
_colorHighlight = config.Bind<string>("Color", "Highlight", "red", "The color to use to highlight important information in the message (e.g. the number of a roll, or winner of an award).");
_msgQueueSize = config.Bind<int>("Advanced", "MessageQueueSize", 7, new ConfigDescription("How many messages to keep in the history buffer that gets sent to clients after a 'roll', 'award', or 'results' command.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
_msgQueueTimeoutSecs = config.Bind<int>("Advanced", "MessageQueueTimeout", 60, new ConfigDescription("How many seconds messages should remain in the history buffer that gets sent to clients. Set this to 0 to disable time based history pruning (the buffer will fill to the MessageQueueSize set above before being replaced).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 600), Array.Empty<object>()));
_msgLocation = config.Bind<MessageType>("Advanced", "MessageLocation", (MessageType)1, "Location to display 'roll' and 'award' messages. Top left is more out of the way, center lasts longer and is easier to read.");
_msgQueueSize.SettingChanged += OnSettingsChanged;
_msgQueueTimeoutSecs.SettingChanged += OnSettingsChanged;
_msgLocation.SettingChanged += OnSettingsChanged;
}
public void Dispose()
{
if (_msgQueueSize != null)
{
_msgQueueSize.SettingChanged -= OnSettingsChanged;
}
if (_msgQueueTimeoutSecs != null)
{
_msgQueueTimeoutSecs.SettingChanged -= OnSettingsChanged;
}
if (_msgLocation != null)
{
_msgLocation.SettingChanged -= OnSettingsChanged;
}
}
private void OnSettingsChanged(object sender, EventArgs args)
{
ZRoutedRpcPatch.OnMessageHelperSettingsChanged();
}
}
}
namespace ServerDiceRoll.Patches
{
[HarmonyPatch(typeof(ZRoutedRpc))]
public class ZRoutedRpcPatch
{
public static int SayHashCode = StringExtensionMethods.GetStableHashCode("Say");
private static MessageHelper s_messageHelper = new MessageHelper();
private static PluginConfig Config => VSDR.Instance.PluginConfig;
[HarmonyPatch("HandleRoutedRPC")]
[HarmonyPostfix]
public static void OnHandleRoutedRPC(RoutedRPCData data)
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected O, but got Unknown
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected O, but got Unknown
if (!ZNet.instance.IsServer() || data.m_methodHash != SayHashCode)
{
return;
}
try
{
ZPackage val = new ZPackage(data.m_parameters.GetArray());
val.SetPos(0);
val.ReadInt();
UserInfo val2 = new UserInfo();
val2.Deserialize(ref val);
string text = val.ReadString() ?? "";
text = text.Trim();
VSDR.Instance.Log.LogDebug((object)("Testing message " + text));
if (text.StartsWith(Config.CmdPrefix + Config.CmdRoll))
{
HandleRoll(data.m_senderPeerID, val2.Name, text);
}
else if (text.StartsWith(Config.CmdPrefix + Config.CmdAward))
{
HandleAward(data.m_senderPeerID, val2.Name, text);
}
else if (text.StartsWith(Config.CmdPrefix + Config.CmdResults))
{
s_messageHelper.ResendBroadcasts(data.m_senderPeerID);
}
}
catch (Exception arg)
{
VSDR.Instance.Log.LogError((object)$"Exception while handling roll/award action: {arg}");
}
}
internal static void OnMessageHelperSettingsChanged()
{
s_messageHelper = new MessageHelper();
}
private static void HandleRoll(long triggeringPeerId, string triggeringPlayer, string message)
{
string[] array = message.Split(new char[1] { ' ' });
int result = Config.RollMax;
if (array.Length > 1)
{
string text = array[1];
if (!int.TryParse(text, out result) || result < 1)
{
s_messageHelper.MessagePeer(triggeringPeerId, BuildRollUsage(text + " is not a positive integer!"));
return;
}
}
string text2 = triggeringPlayer;
int num = Random.RandomRangeInt(1, result + 1);
string arg = $"{num}";
if (!string.IsNullOrWhiteSpace(Config.ColorHighlight))
{
arg = $"<color=\"{Config.ColorHighlight}\">{num}</color>";
text2 = "<color=\"" + Config.ColorHighlight + "\">" + text2 + "</color>";
}
string text3 = $"{text2} rolled a {result} sided die and got a {arg}!";
if (!string.IsNullOrWhiteSpace(Config.ColorRoll))
{
text3 = "<color=\"" + Config.ColorRoll + "\">" + text3 + "</color>";
}
s_messageHelper.Broadcast(text3);
}
private static void HandleAward(long triggeringPeerId, string triggeringPlayer, string message)
{
string[] array = message.Split(new char[1] { ' ' });
if (array.Length < 2 || string.IsNullOrEmpty(array[1]))
{
s_messageHelper.MessagePeer(triggeringPeerId, BuildAwardUsage("Missing list of recipients!"));
return;
}
string[] array2 = (from n in array[1].Split(new char[1] { ',' })
where !string.IsNullOrWhiteSpace(n)
select n).ToArray();
string text = string.Join(", ", array2.Take(array2.Length - 1)) + ", or " + array2.Last();
int num = Random.RandomRangeInt(0, array2.Length);
string text2 = array2[num] ?? "";
if (!string.IsNullOrWhiteSpace(Config.ColorHighlight))
{
text2 = "<color=\"" + Config.ColorHighlight + "\">" + text2 + "</color>";
}
string text3 = "";
string text4 = "picks ";
if (array.Length > 2)
{
string text5 = string.Join(" ", array.Skip(2));
if (!string.IsNullOrWhiteSpace(Config.ColorHighlight))
{
text5 = "<color=\"" + Config.ColorHighlight + "\">" + text5 + "</color>";
}
text3 = " of \"" + text5 + "\"";
text4 = "awards \"" + text5 + "\" to ";
}
string text6 = triggeringPlayer + " asks Odin to decide if " + text + " is worthy" + text3 + "...";
string text7 = "Odin " + text4 + text2 + "!";
string text8 = text6 + "\n" + text7;
if (!string.IsNullOrWhiteSpace(Config.ColorAward))
{
text8 = "<color=\"" + Config.ColorAward + "\">" + text8 + "</color>";
}
s_messageHelper.Broadcast(text8);
}
private static string BuildRollUsage(string problemDesc)
{
string text = "Usage: " + Config.CmdPrefix + Config.CmdRoll + " <Die Size (optional)>";
string text2 = problemDesc + "\n" + text;
if (!string.IsNullOrWhiteSpace(Config.ColorUsage))
{
text2 = "<color=\"" + Config.ColorUsage + "\">" + text2 + "</color>";
}
return text2;
}
private static string BuildAwardUsage(string problemDesc)
{
string text = "Usage: " + Config.CmdPrefix + Config.CmdAward + " <Person1,Person2,etc (no spaces)> <Name of thing to be awarded (optional)>";
string text2 = problemDesc + "\n" + text;
if (!string.IsNullOrWhiteSpace(Config.ColorUsage))
{
text2 = "<color=\"" + Config.ColorUsage + "\">" + text2 + "</color>";
}
return text2;
}
}
}