Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DiscordConnector Client v1.0.3
DiscordConnector-Client.dll
Decompiled 8 months agousing System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using DiscordConnector.Common; using DiscordConnector.RPC; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("DiscordConnectorClient")] [assembly: AssemblyDescription("Enhances Valheim by sending messages to a Discord Webhook")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("nwesterhausen")] [assembly: AssemblyProduct("DiscordConnectorClient")] [assembly: AssemblyCopyright("© 2025 nwesterhausen")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("E2942C94-F0B8-414E-A920-328E601BFF87")] [assembly: AssemblyFileVersion("1.0.3")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.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 DiscordConnector { [BepInPlugin("nwesterhausen.DiscordConnectorClient", "DiscordConnectorClient", "1.0.3")] public class DiscordConnectorClientPlugin : BaseUnityPlugin { internal const string ModName = "DiscordConnectorClient"; internal const string ModVersion = "1.0.3"; internal const string Author = "nwesterhausen"; private const string ModGuid = "nwesterhausen.DiscordConnectorClient"; private const string LegacyConfigPath = "games.nwest.valheim.discordconnector"; internal const string LegacyModName = "discordconnector"; internal static VdcLogger StaticLogger; private Harmony? _harmony; public DiscordConnectorClientPlugin() { StaticLogger = new VdcLogger(((BaseUnityPlugin)this).Logger, Paths.ConfigPath); } private void Awake() { StaticLogger.LogDebug("Plugin DiscordConnectorClient is loaded!"); _harmony = Harmony.CreateAndPatchAll(typeof(DiscordConnectorClientPlugin).Assembly, "nwesterhausen.DiscordConnectorClient"); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace DiscordConnector.Patches { internal class ChatPatches { [HarmonyPatch(typeof(Chat), "OnNewChatMessage")] internal class OnNewChatMessage { private static void Prefix(ref GameObject go, ref long senderID, ref Vector3 pos, ref Type type, ref UserInfo sender, ref string text) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (senderID != ZNet.GetUID()) { DiscordConnectorClientPlugin.StaticLogger.LogDebug($"Ignoring message from other {senderID} != {ZNet.GetUID()}"); return; } DiscordConnectorClientPlugin.StaticLogger.LogDebug($"User details: name:{sender.Name} DisplayName():{sender.GetDisplayName()} senderID:{senderID} type:{type} text:{text}"); try { ChatMessageDetail chatMessageDetail = new ChatMessageDetail(pos, type, text); ZPackage val = chatMessageDetail.ToZPackage(); long serverPeerID = ZRoutedRpc.instance.GetServerPeerID(); try { ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerID, "DiscordConnector_OnNewChatMessage", new object[1] { val }); DiscordConnectorClientPlugin.StaticLogger.LogDebug($"Sent encoded chat message to server {chatMessageDetail} in {val.Size()}B"); } catch (Exception ex) { DiscordConnectorClientPlugin.StaticLogger.LogError("Failed to send chat message to server"); throw ex; } } catch (Exception ex2) { DiscordConnectorClientPlugin.StaticLogger.LogError("Failed to encode chat message"); DiscordConnectorClientPlugin.StaticLogger.LogDebug(ex2.ToString()); } } } internal const string ArrivalShout = "I have arrived!"; } internal class GamePatches { [HarmonyPatch(typeof(Game), "Start")] internal static class GameStartPatch { private static void Prefix() { ZRoutedRpc.instance.Register<ZPackage>("DiscordConnector_OnNewChatMessage", (Action<long, ZPackage>)Client.RPC_OnNewChatMessage); DiscordConnectorClientPlugin.StaticLogger.LogInfo("Registered RPC: DiscordConnector_OnNewChatMessage"); } } } } namespace DiscordConnector.RPC { internal class ChatMessageDetail { private static readonly char Separator = '|'; private static readonly int ExpectedParts = 5; public Vector3 Pos { get; } public Type TalkerType { get; } public string Text { get; } public static string EmptyTextMessage { get; } = "#empty#"; public ChatMessageDetail(Vector3 pos, Type type, string text) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Pos = pos; TalkerType = type; Text = text; base..ctor(); } private string EncodeSelf() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected I4, but got Unknown string text = $"{Pos.x}{Separator}{Pos.y}{Separator}{Pos.z}"; string text2 = ((int)TalkerType).ToString(); return $"{text}{Separator}{text2}{Separator}{Text}"; } private static ChatMessageDetail DecodeSelf(string encoded) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) string[] array = encoded.Split(new char[1] { Separator }); if (array.Length != ExpectedParts) { throw new ArgumentException($"Invalid number of parts in encoded string ({array.Length} instead of {ExpectedParts})"); } if (!float.TryParse(array[0], out var result)) { throw new ArgumentException("Failed to parse Pos.X component: " + array[0]); } if (!float.TryParse(array[1], out var result2)) { throw new ArgumentException("Failed to parse Pos.Y component: " + array[1]); } if (!float.TryParse(array[2], out var result3)) { throw new ArgumentException("Failed to parse Pos.Z component: " + array[2]); } Vector3 pos = new Vector3(result, result2, result3); if (!int.TryParse(array[3], out var result4)) { throw new ArgumentException("Failed to parse Talker.Type component: " + array[3]); } if (!Enum.IsDefined(typeof(Type), result4)) { throw new ArgumentException($"Invalid Talker.Type value: {result4}"); } Type type = (Type)result4; return new ChatMessageDetail(pos, type, array[4]); } public ZPackage ToZPackage() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown string text = EncodeSelf(); try { ZPackage val = new ZPackage(); val.Write(text); return val; } catch (Exception innerException) { throw new Exception("Failed to encode ZPackage with " + text, innerException); } } public static ChatMessageDetail FromZPackage(ZPackage? pkg) { if (pkg == null || pkg.Size() == 0) { throw new ArgumentException("ZPackage is null or empty"); } try { return DecodeSelf(pkg.ReadString()); } catch (Exception innerException) { throw new Exception("Failed to decode ZPackage", innerException); } } public override string ToString() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) return $"ChatMessageDetail: Pos={Pos}, Type={TalkerType}, Text={Text}"; } } internal static class Client { public static void RPC_OnNewChatMessage(long sender, ZPackage pkg) { } } internal static class Common { public const string RPC_OnNewChatMessage = "DiscordConnector_OnNewChatMessage"; } } namespace DiscordConnector.Common { internal sealed class VdcLogger { private const string LogName = "vdc.log"; private const int MaxLogFiles = 5; private static ManualLogSource s_logger; private static string s_logFilePath; private bool _logDebugMessages; public VdcLogger(ManualLogSource logger, string basePath) { s_logger = logger; s_logFilePath = Path.Combine(basePath, "vdc.log"); InitializeLogFile(); s_logger.LogInfo((object)"Logger initialized."); } public void SetLogLevel(bool logDebugMessages) { _logDebugMessages = logDebugMessages; } private static void InitializeLogFile() { if (!File.Exists(s_logFilePath)) { return; } for (int num = 5; num > 1; num--) { string text = $"{s_logFilePath}.{num}"; string text2 = $"{s_logFilePath}.{num - 1}"; if (File.Exists(text)) { try { File.Delete(text); } catch (Exception ex) { s_logger.LogError((object)("Error deleting old log file: " + ex.Message)); } } if (File.Exists(text2)) { try { File.Move(text2, text); } catch (Exception ex2) { s_logger.LogError((object)("Error moving log file: " + ex2.Message)); } } } try { File.Move(s_logFilePath, s_logFilePath + ".1"); } catch (Exception ex3) { s_logger.LogError((object)("Error moving log file: " + ex3.Message)); } s_logger.LogInfo((object)"Existing log files versioned."); } private async Task LogToFileAsync(string severity, string message) { try { using StreamWriter writer = new StreamWriter(s_logFilePath, append: true); await writer.WriteLineAsync($"{DateTime.Now} [{severity}]: {message}"); } catch (Exception ex) { s_logger.LogError((object)("Error writing to log file: " + ex.Message)); } } public async Task LogDebugAsync(string message) { await LogToFileAsync("DEBUG", message); if (_logDebugMessages) { s_logger.LogInfo((object)message); } } public async Task LogInfoAsync(string message) { await LogToFileAsync("INFO", message); s_logger.LogInfo((object)message); } public async Task LogWarningAsync(string message) { await LogToFileAsync("WARNING", message); s_logger.LogWarning((object)message); } public async Task LogErrorAsync(string message) { await LogToFileAsync("ERROR", message); s_logger.LogError((object)message); } public async Task LogFatalAsync(string message) { await LogToFileAsync("FATAL", message); s_logger.LogFatal((object)message); } public void LogDebug(string message) { LogDebugAsync(message).GetAwaiter().GetResult(); } public void LogInfo(string message) { LogInfoAsync(message).GetAwaiter().GetResult(); } public void LogWarning(string message) { LogWarningAsync(message).GetAwaiter().GetResult(); } public void LogError(string message) { LogErrorAsync(message).GetAwaiter().GetResult(); } public void LogFatal(string message) { LogFatalAsync(message).GetAwaiter().GetResult(); } } }