Decompiled source of DiscordConnector Client v1.0.1

DiscordConnector-Client.dll

Decompiled 3 days ago
using 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.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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.1")]
	public class DiscordConnectorClientPlugin : BaseUnityPlugin
	{
		internal const string ModName = "DiscordConnectorClient";

		internal const string ModVersion = "1.0.1";

		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();
		}
	}
}