using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Extensions;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("discordScreenshots")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("discordScreenshots")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("CA695DDD-2CE8-407B-B7A2-9FB6D3783286")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
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;
}
}
}
[HarmonyPatch(typeof(Player), "OnDamaged")]
public static class RemoveDamageFlashOnDeath
{
[HarmonyPostfix]
private static bool Prefix(Player __instance, HitData hit)
{
if (hit.GetTotalDamage() >= ((Character)__instance).GetHealth())
{
return false;
}
return true;
}
}
namespace discordScreenshots
{
public class BepinexConfiguration
{
public static ConfigFile Config;
public static ConfigEntry<string> WebhookURL;
public static ConfigEntry<string> WebhookUsername;
public static ConfigEntry<string> WebhookAvatarURL;
public static ConfigEntry<string> DeathMessage;
public static ConfigEntry<KeyCode> ScreenshotHotkey;
public static void GenerateConfigs(ConfigFile configFile)
{
Config = configFile;
ScreenshotHotkey = ConfigFileExtensions.BindConfig<KeyCode>(Config, "Screenshot", "Hotkey", (KeyCode)293, "The hotkey to take a screenshot and send to Discord.", false, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
WebhookURL = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "URL", "", "The URL of the Discord webhook to send messages to.", true, (int?)2, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
WebhookUsername = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "Username", "Valheim Death Bot", "The username of the Discord webhook to send messages to.", true, (int?)3, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
WebhookAvatarURL = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "AvatarURL", "", "The avatar URL of the Discord webhook to send messages to.", true, (int?)4, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
DeathMessage = ConfigFileExtensions.BindConfig<string>(Config, "Death Screenshot", "Message", "met their demise! Final moments captured...", "The message to send with death screenshots (player name will be prepended automatically).", true, (int?)5, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
}
}
public class SimpleDiscordWebhook
{
private readonly string _webhookUrl;
private readonly string? _username;
private readonly string? _avatarUrl;
public SimpleDiscordWebhook(string webhookUrl, string? username = null, string? avatarUrl = null)
{
if (string.IsNullOrEmpty(webhookUrl))
{
throw new ArgumentException("Webhook URL cannot be null or empty", "webhookUrl");
}
_webhookUrl = webhookUrl;
_username = username;
_avatarUrl = avatarUrl;
}
public async Task SendMessageAsync(string message)
{
if (string.IsNullOrEmpty(message))
{
throw new ArgumentException("Message cannot be null or empty", "message");
}
SimpleWebhookPayload payload = new SimpleWebhookPayload
{
content = message,
username = _username,
avatar_url = _avatarUrl
};
string jsonPayload = JsonConvert.SerializeObject((object)payload);
await SendPayloadAsync(jsonPayload);
}
public void SendMessage(string message)
{
SendMessageAsync(message).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
}
public async Task SendScreenshotAsync(string? message = null, string? filename = null)
{
string filename2 = filename;
string message2 = message;
try
{
if (string.IsNullOrEmpty(filename2))
{
filename2 = $"valheim_screenshot_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
}
else if (!filename2.EndsWith(".png"))
{
filename2 += ".png";
}
Texture2D screenshot = ScreenCapture.CaptureScreenshotAsTexture();
if ((Object)(object)screenshot == (Object)null)
{
throw new Exception("Failed to capture screenshot - returned null texture");
}
byte[] pngData = ImageConversion.EncodeToPNG(screenshot);
Object.DestroyImmediate((Object)(object)screenshot);
if (pngData == null || pngData.Length == 0)
{
throw new Exception("Failed to encode screenshot to PNG");
}
Debug.Log((object)$"Screenshot captured and encoded - {pngData.Length} bytes, uploading...");
await Task.Run(async delegate
{
await SendFileAsync(pngData, filename2, message2);
});
}
catch (Exception ex2)
{
Exception ex = ex2;
throw new Exception("Error capturing and sending screenshot: " + ex.Message, ex);
}
}
public Texture2D CaptureScreenshot()
{
Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
if ((Object)(object)val == (Object)null)
{
throw new Exception("Failed to capture screenshot - returned null texture");
}
return val;
}
public byte[] ProcessScreenshot(Texture2D screenshot)
{
byte[] result = ImageConversion.EncodeToPNG(screenshot);
Object.DestroyImmediate((Object)(object)screenshot);
return result;
}
public async Task SendFileAsync(byte[] fileData, string filename, string? message = null)
{
if (fileData == null || fileData.Length == 0)
{
throw new ArgumentException("File data cannot be null or empty", "fileData");
}
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentException("Filename cannot be null or empty", "filename");
}
string boundary = "----formdata-discord-" + DateTime.Now.Ticks.ToString("x");
using MemoryStream memoryStream = new MemoryStream();
await WriteMultipartFormDataAsync(memoryStream, boundary, fileData, filename, message);
byte[] formData = memoryStream.ToArray();
await SendMultipartPayloadAsync(formData, boundary);
}
private async Task SendPayloadAsync(string jsonPayload)
{
try
{
byte[] byteArray = Encoding.UTF8.GetBytes(jsonPayload);
WebRequest request = WebRequest.Create(_webhookUrl);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = byteArray.Length;
using (Stream dataStream = request.GetRequestStream())
{
await dataStream.WriteAsync(byteArray, 0, byteArray.Length);
}
using WebResponse response = request.GetResponse();
if (response is HttpWebResponse httpResponse && httpResponse.StatusCode != HttpStatusCode.NoContent && httpResponse.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"Discord webhook returned status: {httpResponse.StatusCode}");
}
using Stream responseStream = response.GetResponseStream();
if (responseStream == null)
{
return;
}
using StreamReader streamReader = new StreamReader(responseStream);
string responseText = await streamReader.ReadToEndAsync();
if (!string.IsNullOrEmpty(responseText))
{
Debug.Log((object)("Discord response: " + responseText));
}
}
catch (WebException ex2)
{
string errorMessage = "Failed to send webhook message";
WebResponse response2 = ex2.Response;
if (response2 is HttpWebResponse errorResponse)
{
using Stream errorStream = errorResponse.GetResponseStream();
if (errorStream != null)
{
using StreamReader reader = new StreamReader(errorStream);
errorMessage += string.Format(arg1: await reader.ReadToEndAsync(), format: ": {0} - {1}", arg0: errorResponse.StatusCode);
}
}
throw new Exception(errorMessage, ex2);
}
catch (Exception ex3)
{
Exception ex = ex3;
throw new Exception("Error sending Discord webhook: " + ex.Message, ex);
}
}
private async Task SendMultipartPayloadAsync(byte[] formData, string boundary)
{
try
{
WebRequest request = WebRequest.Create(_webhookUrl);
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.ContentLength = formData.Length;
using (Stream dataStream = request.GetRequestStream())
{
await dataStream.WriteAsync(formData, 0, formData.Length);
}
using WebResponse response = request.GetResponse();
if (response is HttpWebResponse httpResponse && httpResponse.StatusCode != HttpStatusCode.NoContent && httpResponse.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"Discord webhook returned status: {httpResponse.StatusCode}");
}
using Stream responseStream = response.GetResponseStream();
if (responseStream == null)
{
return;
}
using StreamReader streamReader = new StreamReader(responseStream);
string responseText = await streamReader.ReadToEndAsync();
if (!string.IsNullOrEmpty(responseText))
{
Debug.Log((object)("Discord file upload response: " + responseText));
}
}
catch (WebException ex2)
{
string errorMessage = "Failed to send webhook file";
WebResponse response2 = ex2.Response;
if (response2 is HttpWebResponse errorResponse)
{
using Stream errorStream = errorResponse.GetResponseStream();
if (errorStream != null)
{
using StreamReader reader = new StreamReader(errorStream);
errorMessage += string.Format(arg1: await reader.ReadToEndAsync(), format: ": {0} - {1}", arg0: errorResponse.StatusCode);
}
}
throw new Exception(errorMessage, ex2);
}
catch (Exception ex3)
{
Exception ex = ex3;
throw new Exception("Error sending Discord webhook file: " + ex.Message, ex);
}
}
private async Task WriteMultipartFormDataAsync(Stream stream, string boundary, byte[] fileData, string filename, string? message)
{
string newLine = "\r\n";
byte[] boundaryBytes = Encoding.UTF8.GetBytes("--" + boundary + newLine);
await stream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
string fileHeader = "Content-Disposition: form-data; name=\"files[0]\"; filename=\"" + filename + "\"" + newLine + "Content-Type: image/png" + newLine + newLine;
byte[] fileHeaderBytes = Encoding.UTF8.GetBytes(fileHeader);
await stream.WriteAsync(fileHeaderBytes, 0, fileHeaderBytes.Length);
await stream.WriteAsync(fileData, 0, fileData.Length);
byte[] newLineBytes = Encoding.UTF8.GetBytes(newLine);
await stream.WriteAsync(newLineBytes, 0, newLineBytes.Length);
if (!string.IsNullOrEmpty(message))
{
await stream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
SimpleWebhookPayload payload = new SimpleWebhookPayload
{
content = message,
username = _username,
avatar_url = _avatarUrl
};
string jsonPayload = JsonConvert.SerializeObject((object)payload);
string jsonHeader = "Content-Disposition: form-data; name=\"payload_json\"" + newLine + "Content-Type: application/json" + newLine + newLine;
byte[] jsonHeaderBytes = Encoding.UTF8.GetBytes(jsonHeader);
await stream.WriteAsync(jsonHeaderBytes, 0, jsonHeaderBytes.Length);
byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonPayload);
await stream.WriteAsync(jsonBytes, 0, jsonBytes.Length);
await stream.WriteAsync(newLineBytes, 0, newLineBytes.Length);
}
byte[] closingBoundary = Encoding.UTF8.GetBytes("--" + boundary + "--" + newLine);
await stream.WriteAsync(closingBoundary, 0, closingBoundary.Length);
}
public static async Task SendQuickMessageAsync(string webhookUrl, string message, string? username = null, string? avatarUrl = null)
{
SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(webhookUrl, username, avatarUrl);
await webhook.SendMessageAsync(message);
}
public static void SendQuickMessage(string webhookUrl, string message, string? username = null, string? avatarUrl = null)
{
SendQuickMessageAsync(webhookUrl, message, username, avatarUrl).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
}
public static async Task SendQuickScreenshotAsync(string webhookUrl, string? message = null, string? username = null, string? avatarUrl = null, string? filename = null)
{
SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(webhookUrl, username, avatarUrl);
await webhook.SendScreenshotAsync(message, filename);
}
public static void SendQuickScreenshot(string webhookUrl, string? message = null, string? username = null, string? avatarUrl = null, string? filename = null)
{
SendQuickScreenshotAsync(webhookUrl, message, username, avatarUrl, filename).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
}
}
internal class SimpleWebhookPayload
{
public string? content { get; set; }
public string? username { get; set; }
public string? avatar_url { get; set; }
}
[BepInPlugin("warpalicious.discordScreenshots", "discordScreenshots", "1.4.0")]
public class discordScreenshotsPlugin : BaseUnityPlugin
{
private const string ModName = "discordScreenshots";
private const string ModVersion = "1.4.0";
private const string Author = "warpalicious";
private const string ModGUID = "warpalicious.discordScreenshots";
private static string ConfigFileName = "warpalicious.discordScreenshots.cfg";
private static string ConfigFileFullPath;
private readonly Harmony HarmonyInstance = new Harmony("warpalicious.discordScreenshots");
public static readonly ManualLogSource TemplateLogger;
public static AssetBundle assetBundle;
private DateTime _lastReloadTime;
private const long RELOAD_DELAY = 10000000L;
public void Awake()
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
HarmonyInstance.PatchAll(executingAssembly);
SetupWatcher();
BepinexConfiguration.GenerateConfigs(((BaseUnityPlugin)this).Config);
}
public static void LoadAssetBundle()
{
assetBundle = AssetUtils.LoadAssetBundleFromResources("discordscreenshots", Assembly.GetExecutingAssembly());
}
private void OnDestroy()
{
((BaseUnityPlugin)this).Config.Save();
}
private void SetupWatcher()
{
_lastReloadTime = DateTime.Now;
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
fileSystemWatcher.Changed += ReadConfigValues;
fileSystemWatcher.Created += ReadConfigValues;
fileSystemWatcher.Renamed += ReadConfigValues;
fileSystemWatcher.IncludeSubdirectories = true;
fileSystemWatcher.EnableRaisingEvents = true;
}
private void ReadConfigValues(object sender, FileSystemEventArgs e)
{
DateTime now = DateTime.Now;
long num = now.Ticks - _lastReloadTime.Ticks;
if (File.Exists(ConfigFileFullPath) && num >= 10000000)
{
try
{
TemplateLogger.LogInfo((object)"Attempting to reload configuration...");
((BaseUnityPlugin)this).Config.Reload();
TemplateLogger.LogInfo((object)"Configuration reloaded successfully!");
}
catch
{
TemplateLogger.LogError((object)("There was an issue loading " + ConfigFileName));
return;
}
_lastReloadTime = now;
if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsDedicated())
{
TemplateLogger.LogInfo((object)"Updating runtime configurations...");
}
}
}
static discordScreenshotsPlugin()
{
string configPath = Paths.ConfigPath;
char directorySeparatorChar = Path.DirectorySeparatorChar;
ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
TemplateLogger = Logger.CreateLogSource("discordScreenshots");
}
}
}
namespace discordScreenshots.Patches
{
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
public static class DiscordConsoleCommandPatch
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static ConsoleEvent <>9__0_0;
public static ConsoleEvent <>9__0_1;
public static ConsoleEvent <>9__0_2;
internal void <Postfix>b__0_0(ConsoleEventArgs args)
{
<>c__DisplayClass0_0 CS$<>8__locals0 = new <>c__DisplayClass0_0();
if (args.Length < 2)
{
Terminal context = args.Context;
if (context != null)
{
context.AddString("Usage: discord <message>");
}
Terminal context2 = args.Context;
if (context2 != null)
{
context2.AddString("Example: discord Hello from Valheim!");
}
return;
}
CS$<>8__locals0.message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
if (string.IsNullOrWhiteSpace(CS$<>8__locals0.message))
{
Terminal context3 = args.Context;
if (context3 != null)
{
context3.AddString("Message cannot be empty");
}
return;
}
CS$<>8__locals0.playerName = "Server";
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
CS$<>8__locals0.playerName = Player.m_localPlayer.GetPlayerName();
}
Terminal context4 = args.Context;
if (context4 != null)
{
context4.AddString("Sending message to Discord...");
}
Task.Run(async delegate
{
try
{
string formattedMessage = "\ud83d\udcac **" + CS$<>8__locals0.playerName + "**: " + CS$<>8__locals0.message;
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, formattedMessage, "Valheim Console");
Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("Failed to send Discord message: " + ex.Message));
}
});
}
internal void <Postfix>b__0_1(ConsoleEventArgs args)
{
<>c__DisplayClass0_1 CS$<>8__locals0 = new <>c__DisplayClass0_1();
Terminal context = args.Context;
if (context != null)
{
context.AddString("Testing Discord webhook connection...");
}
CS$<>8__locals0.playerName = "Server";
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
CS$<>8__locals0.playerName = Player.m_localPlayer.GetPlayerName();
}
Task.Run(async delegate
{
try
{
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, "\ud83e\uddea Discord webhook test from **" + CS$<>8__locals0.playerName + "** - Connection working!", "Valheim Test Bot");
Debug.Log((object)"Discord webhook test message sent successfully!");
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("Discord webhook test failed: " + ex.Message));
}
});
}
internal void <Postfix>b__0_2(ConsoleEventArgs args)
{
<>c__DisplayClass0_2 <>c__DisplayClass0_ = new <>c__DisplayClass0_2
{
playerName = "Unknown Player"
};
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
<>c__DisplayClass0_.playerName = Player.m_localPlayer.GetPlayerName();
}
<>c__DisplayClass0_.message = null;
if (args.Length > 1)
{
<>c__DisplayClass0_.message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
}
if (string.IsNullOrEmpty(<>c__DisplayClass0_.message))
{
<>c__DisplayClass0_.message = "\ud83d\udcf8 **" + <>c__DisplayClass0_.playerName + "** took a screenshot!";
}
Terminal context = args.Context;
if (context != null)
{
context.AddString("Capturing screenshot...");
}
try
{
<>c__DisplayClass0_3 CS$<>8__locals0 = new <>c__DisplayClass0_3();
CS$<>8__locals0.CS$<>8__locals1 = <>c__DisplayClass0_;
CS$<>8__locals0.webhook = new SimpleDiscordWebhook(BepinexConfiguration.WebhookURL.Value, "Valheim Screenshots");
CS$<>8__locals0.filename = $"{CS$<>8__locals0.CS$<>8__locals1.playerName}_screenshot_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
if ((Object)(object)val == (Object)null)
{
throw new Exception("Failed to capture screenshot");
}
CS$<>8__locals0.pngData = ImageConversion.EncodeToPNG(val);
Object.DestroyImmediate((Object)(object)val);
if (CS$<>8__locals0.pngData == null || CS$<>8__locals0.pngData.Length == 0)
{
throw new Exception("Failed to encode screenshot to PNG");
}
Terminal context2 = args.Context;
if (context2 != null)
{
context2.AddString("Screenshot captured, uploading to Discord...");
}
Task.Run(async delegate
{
try
{
await CS$<>8__locals0.webhook.SendFileAsync(CS$<>8__locals0.pngData, CS$<>8__locals0.filename, CS$<>8__locals0.CS$<>8__locals1.message);
Debug.Log((object)("Screenshot uploaded to Discord for " + CS$<>8__locals0.CS$<>8__locals1.playerName));
}
catch (Exception ex3)
{
Exception ex2 = ex3;
Debug.LogError((object)("Failed to upload screenshot: " + ex2.Message));
}
});
}
catch (Exception ex)
{
Terminal context3 = args.Context;
if (context3 != null)
{
context3.AddString("Error: " + ex.Message);
}
Debug.LogError((object)("Failed to capture screenshot: " + ex.Message));
}
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public string playerName;
public string message;
internal async Task <Postfix>b__3()
{
try
{
string formattedMessage = "\ud83d\udcac **" + playerName + "**: " + message;
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, formattedMessage, "Valheim Console");
Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("Failed to send Discord message: " + ex.Message));
}
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass0_1
{
public string playerName;
internal async Task <Postfix>b__4()
{
try
{
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, "\ud83e\uddea Discord webhook test from **" + playerName + "** - Connection working!", "Valheim Test Bot");
Debug.Log((object)"Discord webhook test message sent successfully!");
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("Discord webhook test failed: " + ex.Message));
}
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass0_2
{
public string message;
public string playerName;
}
[CompilerGenerated]
private sealed class <>c__DisplayClass0_3
{
public SimpleDiscordWebhook webhook;
public byte[] pngData;
public string filename;
public <>c__DisplayClass0_2 CS$<>8__locals1;
internal async Task <Postfix>b__5()
{
try
{
await webhook.SendFileAsync(pngData, filename, CS$<>8__locals1.message);
Debug.Log((object)("Screenshot uploaded to Discord for " + CS$<>8__locals1.playerName));
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("Failed to upload screenshot: " + ex.Message));
}
}
}
[HarmonyPostfix]
private static void Postfix()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: 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_002a: Expected O, but got Unknown
//IL_006b: 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_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Expected O, but got Unknown
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Expected O, but got Unknown
object obj = <>c.<>9__0_0;
if (obj == null)
{
ConsoleEvent val = delegate(ConsoleEventArgs args)
{
if (args.Length < 2)
{
Terminal context5 = args.Context;
if (context5 != null)
{
context5.AddString("Usage: discord <message>");
}
Terminal context6 = args.Context;
if (context6 != null)
{
context6.AddString("Example: discord Hello from Valheim!");
}
}
else
{
string message2 = string.Join(" ", args.Args, 1, args.Args.Length - 1);
if (string.IsNullOrWhiteSpace(message2))
{
Terminal context7 = args.Context;
if (context7 != null)
{
context7.AddString("Message cannot be empty");
}
}
else
{
string playerName3 = "Server";
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
playerName3 = Player.m_localPlayer.GetPlayerName();
}
Terminal context8 = args.Context;
if (context8 != null)
{
context8.AddString("Sending message to Discord...");
}
Task.Run(async delegate
{
try
{
string formattedMessage = "\ud83d\udcac **" + playerName3 + "**: " + message2;
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, formattedMessage, "Valheim Console");
Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
}
catch (Exception ex7)
{
Exception ex6 = ex7;
Debug.LogError((object)("Failed to send Discord message: " + ex6.Message));
}
});
}
}
};
<>c.<>9__0_0 = val;
obj = (object)val;
}
new ConsoleCommand("discord", "sends a message to Discord webhook. Usage: discord <message>", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
object obj2 = <>c.<>9__0_1;
if (obj2 == null)
{
ConsoleEvent val2 = delegate(ConsoleEventArgs args)
{
Terminal context4 = args.Context;
if (context4 != null)
{
context4.AddString("Testing Discord webhook connection...");
}
string playerName2 = "Server";
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
playerName2 = Player.m_localPlayer.GetPlayerName();
}
Task.Run(async delegate
{
try
{
await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.WebhookURL.Value, "\ud83e\uddea Discord webhook test from **" + playerName2 + "** - Connection working!", "Valheim Test Bot");
Debug.Log((object)"Discord webhook test message sent successfully!");
}
catch (Exception ex5)
{
Exception ex4 = ex5;
Debug.LogError((object)("Discord webhook test failed: " + ex4.Message));
}
});
};
<>c.<>9__0_1 = val2;
obj2 = (object)val2;
}
new ConsoleCommand("discordtest", "tests the Discord webhook connection", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
object obj3 = <>c.<>9__0_2;
if (obj3 == null)
{
ConsoleEvent val3 = delegate(ConsoleEventArgs args)
{
string playerName = "Unknown Player";
if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
{
playerName = Player.m_localPlayer.GetPlayerName();
}
string message = null;
if (args.Length > 1)
{
message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
}
if (string.IsNullOrEmpty(message))
{
message = "\ud83d\udcf8 **" + playerName + "** took a screenshot!";
}
Terminal context = args.Context;
if (context != null)
{
context.AddString("Capturing screenshot...");
}
try
{
SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(BepinexConfiguration.WebhookURL.Value, "Valheim Screenshots");
string filename = $"{playerName}_screenshot_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
Texture2D val4 = ScreenCapture.CaptureScreenshotAsTexture();
if ((Object)(object)val4 == (Object)null)
{
throw new Exception("Failed to capture screenshot");
}
byte[] pngData = ImageConversion.EncodeToPNG(val4);
Object.DestroyImmediate((Object)(object)val4);
if (pngData == null || pngData.Length == 0)
{
throw new Exception("Failed to encode screenshot to PNG");
}
Terminal context2 = args.Context;
if (context2 != null)
{
context2.AddString("Screenshot captured, uploading to Discord...");
}
Task.Run(async delegate
{
try
{
await webhook.SendFileAsync(pngData, filename, message);
Debug.Log((object)("Screenshot uploaded to Discord for " + playerName));
}
catch (Exception ex3)
{
Exception ex2 = ex3;
Debug.LogError((object)("Failed to upload screenshot: " + ex2.Message));
}
});
}
catch (Exception ex)
{
Terminal context3 = args.Context;
if (context3 != null)
{
context3.AddString("Error: " + ex.Message);
}
Debug.LogError((object)("Failed to capture screenshot: " + ex.Message));
}
};
<>c.<>9__0_2 = val3;
obj3 = (object)val3;
}
new ConsoleCommand("discordscreenshot", "captures and sends a screenshot to Discord", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
}
}
[HarmonyPatch(typeof(Player), "Update")]
public static class HotkeyScreenshotPatch
{
[HarmonyPostfix]
private static void Postfix(Player __instance)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Input.GetKeyDown(BepinexConfiguration.ScreenshotHotkey.Value))
{
string text = __instance.GetPlayerName();
if (string.IsNullOrEmpty(text))
{
text = "Unknown Player";
}
Debug.Log((object)("HotkeyScreenshotPatch: " + text + " pressed screenshot hotkey"));
string message = "\ud83d\udcf8 **" + text + "** captured this screenshot!";
string filename = $"{text}_screenshot_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
SimpleDiscordWebhook.SendQuickScreenshotAsync(BepinexConfiguration.WebhookURL.Value, message, BepinexConfiguration.WebhookUsername.Value, BepinexConfiguration.WebhookAvatarURL.Value, filename);
}
}
catch (Exception ex)
{
Debug.LogError((object)("HotkeyScreenshotPatch: Error: " + ex.Message));
}
}
}
public static class PlayerDeathScreenshotPatch
{
internal static Texture2D storedDeathScreenshot;
internal static string storedPlayerName;
internal static DateTime storedDeathTime;
internal static IEnumerator CaptureDeathScreenshotCoroutine(string playerName)
{
yield return (object)new WaitForSeconds(0.1f);
SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(BepinexConfiguration.WebhookURL.Value);
storedDeathScreenshot = webhook.CaptureScreenshot();
storedPlayerName = playerName;
storedDeathTime = DateTime.Now;
Debug.Log((object)("Death screenshot captured and stored for " + playerName));
}
}
[HarmonyPatch(typeof(Humanoid), "OnRagdollCreated")]
public static class PlayerRagdollScreenshotPatch
{
[HarmonyPostfix]
private static void Postfix(Humanoid __instance)
{
try
{
Player val = (Player)(object)((__instance is Player) ? __instance : null);
if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
{
string text = val.GetPlayerName();
if (string.IsNullOrEmpty(text))
{
text = "Unknown Player";
}
Debug.Log((object)("PlayerRagdollScreenshotPatch: " + text + " died - capturing screenshot"));
((MonoBehaviour)val).StartCoroutine(PlayerDeathScreenshotPatch.CaptureDeathScreenshotCoroutine(text));
}
}
catch (Exception ex)
{
Debug.LogError((object)("PlayerRagdollScreenshotPatch: Error: " + ex.Message));
}
}
}
[HarmonyPatch(typeof(Player), "Awake")]
public static class PlayerRespawnScreenshotPatch
{
[HarmonyPostfix]
private static void Postfix(Player __instance)
{
try
{
Debug.Log((object)"PlayerRespawnScreenshotPatch: Awake");
Debug.Log((object)"PlayerRespawnScreenshotPatch: Local player confirmed");
if ((Object)(object)PlayerDeathScreenshotPatch.storedDeathScreenshot != (Object)null)
{
Debug.Log((object)("PlayerRespawnScreenshotPatch: Processing stored death screenshot for " + PlayerDeathScreenshotPatch.storedPlayerName));
ProcessStoredDeathScreenshot();
}
else
{
Debug.Log((object)"PlayerRespawnScreenshotPatch: No stored death screenshot found");
}
}
catch (Exception ex)
{
Debug.LogError((object)("PlayerRespawnScreenshotPatch: Error: " + ex.Message));
}
}
private static void ProcessStoredDeathScreenshot()
{
try
{
SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(BepinexConfiguration.WebhookURL.Value, BepinexConfiguration.WebhookUsername.Value, BepinexConfiguration.WebhookAvatarURL.Value);
byte[] pngData = webhook.ProcessScreenshot(PlayerDeathScreenshotPatch.storedDeathScreenshot);
string deathMessage = "**" + PlayerDeathScreenshotPatch.storedPlayerName + "** " + BepinexConfiguration.DeathMessage.Value;
string filename = $"{PlayerDeathScreenshotPatch.storedPlayerName}_death_{PlayerDeathScreenshotPatch.storedDeathTime:yyyy-MM-dd_HH-mm-ss}.png";
Task.Run(async delegate
{
try
{
await webhook.SendFileAsync(pngData, filename, deathMessage);
Debug.Log((object)("Death screenshot uploaded successfully for " + PlayerDeathScreenshotPatch.storedPlayerName));
}
catch (Exception ex3)
{
Exception ex2 = ex3;
Debug.LogError((object)("Error uploading death screenshot: " + ex2.Message));
}
});
PlayerDeathScreenshotPatch.storedDeathScreenshot = null;
PlayerDeathScreenshotPatch.storedPlayerName = null;
}
catch (Exception ex)
{
Debug.LogError((object)("Error processing stored death screenshot: " + ex.Message));
if ((Object)(object)PlayerDeathScreenshotPatch.storedDeathScreenshot != (Object)null)
{
Object.DestroyImmediate((Object)(object)PlayerDeathScreenshotPatch.storedDeathScreenshot);
PlayerDeathScreenshotPatch.storedDeathScreenshot = null;
PlayerDeathScreenshotPatch.storedPlayerName = null;
}
}
}
}
}