using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 Microsoft.CodeAnalysis; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimReporter { public static class CategoryLoader { public static CategoriesRoot Load(string fileName) { try { string path = Path.Combine(ValheimReporter.DataPath, fileName); if (!File.Exists(path)) { Utils.LogSystem("CategoryLoader", "Load", "JSON file missing: " + fileName, "error", 1); return null; } string text = File.ReadAllText(path); Dictionary categories = JsonConvert.DeserializeObject>(text); return new CategoriesRoot { Categories = categories }; } catch (Exception ex) { Utils.LogSystem("CategoryLoader", "Load", "Error parsing categories: " + ex.Message, "error", 1); return null; } } } public class CategoriesRoot { public Dictionary Categories { get; set; } = new Dictionary(); public Category GetCategory(string id) { Category value; return Categories.TryGetValue(id, out value) ? value : null; } public Category GetCategoryByName(string name) { foreach (Category value in Categories.Values) { if (value.Name == name) { Utils.LogSystem("CategoryLoader", "GetCategoryByName", "Found category: " + value.Name, "log", 4); return value; } } Utils.LogSystem("CategoryLoader", "GetCategoryByName", "Category not found: " + name, "error", 4); return null; } public Subcategory GetSubcategory(string id) { foreach (Category value2 in Categories.Values) { if (value2.HasSubcategories && value2.Subcategories.TryGetValue(id, out var value)) { Utils.LogSystem("CategoryLoader", "GetSubcategory", "Found subcategory: " + value.Name, "log", 4); return value; } } Utils.LogSystem("CategoryLoader", "GetSubcategory", "Subcategory not found: " + id, "error", 4); return null; } public Subcategory GetSubcategoryByName(string name) { foreach (Category value in Categories.Values) { if (!value.HasSubcategories) { continue; } foreach (Subcategory value2 in value.Subcategories.Values) { if (value2.Name == name) { Utils.LogSystem("CategoryLoader", "GetSubcategoryByName", "Found subcategory: " + value2.Name, "log", 4); return value2; } } } Utils.LogSystem("CategoryLoader", "GetSubcategoryByName", "Subcategory not found: " + name, "error", 4); return null; } public List GetList() { return new List(Categories.Values); } } public class Category { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("webhook")] public string Webhook { get; set; } [JsonProperty("required")] public Requirements Required { get; set; } [JsonProperty("subcategories")] public Dictionary Subcategories { get; set; } = new Dictionary(); [JsonIgnore] public bool HasSubcategories => Subcategories != null && Subcategories.Count > 0; } public class Subcategory { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("webhook")] public string Webhook { get; set; } [JsonProperty("required")] public Requirements Required { get; set; } } public class Requirements { [JsonProperty("pictures")] public bool Pictures { get; set; } [JsonProperty("subject")] public bool Subject { get; set; } [JsonProperty("description")] public bool Description { get; set; } } public class Report { [CompilerGenerated] private sealed class d__59 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Report <>4__this; private List.Enumerator <>s__1; private string 5__2; private List 5__3; private string 5__4; private string 5__5; private string 5__6; private string 5__7; private string 5__8; private UnityWebRequest 5__9; private string 5__10; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__59(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if ((uint)(num - -4) <= 1u || num == 1) { try { if (num == -4 || num == 1) { try { } finally { <>m__Finally2(); } } } finally { <>m__Finally1(); } } <>s__1 = default(List.Enumerator); 5__2 = null; 5__3 = null; 5__4 = null; 5__5 = null; 5__6 = null; 5__7 = null; 5__8 = null; 5__9 = null; 5__10 = null; <>1__state = -2; } private bool MoveNext() { //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Invalid comparison between Unknown and I4 //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Expected O, but got Unknown //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>4__this.GenerateReportID(); <>4__this.SaveToLocalLogs(); <>s__1 = <>4__this.Webhook.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -4; if ((int)5__9.result != 1) { 5__10 = ((5__9.downloadHandler != null) ? 5__9.downloadHandler.text : "No response body"); Utils.LogSystem("Report", "Send", "Discord Webhook Error: " + 5__9.error + " | Details: " + 5__10, "error", 1); 5__10 = null; } else { ValheimReporter.playSoundFlag = true; Utils.LogSystem("Report", "Send", "Report successfully sent to Discord.", "success", 3); } <>m__Finally2(); 5__9 = null; 5__3 = null; 5__4 = null; 5__5 = null; 5__6 = null; 5__7 = null; 5__8 = null; 5__2 = null; break; } while (<>s__1.MoveNext()) { 5__2 = <>s__1.Current; if (string.IsNullOrEmpty(5__2)) { Utils.LogSystem("Report", "Send", "Webhook URL is empty!", "error", 1); continue; } 5__3 = new List(); 5__4 = <>4__this.Category ?? "Not Specified"; 5__5 = <>4__this.SubCategory ?? "Not Specified"; 5__6 = (string.IsNullOrEmpty(<>4__this.Subject) ? "Not Specified" : <>4__this.Subject); 5__7 = (string.IsNullOrEmpty(<>4__this.Description) ? "Not Specified" : <>4__this.Description); 5__8 = JsonConvert.SerializeObject((object)new { username = "Valheim Reporter", avatar_url = "https://i.ibb.co/KpNZ88cv/Gemini-Generated-Image-wk1a8fwk1a8fwk1a.png", webhook = (5__2.StartsWith(Utils.ENCRIPTION_HEADER) ? 5__2 : null), embeds = new[] { new { title = "New Report: " + <>4__this.ReportID, color = 15105570, fields = new[] { new { name = "Player", value = <>4__this.PlayerName, inline = true }, new { name = "Steam ID", value = <>4__this.SteamID, inline = true }, new { name = "Discord ID", value = "<@" + <>4__this.DiscordID + ">", inline = true }, new { name = "Coordinates", value = <>4__this.Coordinates, inline = false }, new { name = "Timestamp", value = <>4__this.Date + " | " + <>4__this.Time, inline = false }, new { name = "Category", value = 5__4, inline = true }, new { name = "Subcategory", value = 5__5, inline = true }, new { name = "Subject", value = 5__6, inline = false }, new { name = "Description", value = 5__7, inline = false } }, footer = new { text = "Valheim Reporter System" } } } }); 5__3.Add((IMultipartFormSection)new MultipartFormDataSection("payload_json", 5__8)); if ((Object)(object)<>4__this.Image1 != (Object)null) { 5__3.Add((IMultipartFormSection)new MultipartFormFileSection("file", ImageConversion.EncodeToJPG(<>4__this.Image1, 75), "screenshot1.jpg", "image/jpeg")); } if ((Object)(object)<>4__this.Image2 != (Object)null) { 5__3.Add((IMultipartFormSection)new MultipartFormFileSection("file2", ImageConversion.EncodeToJPG(<>4__this.Image2, 75), "screenshot2.jpg", "image/jpeg")); } 5__9 = UnityWebRequest.Post(5__2.StartsWith(Utils.ENCRIPTION_HEADER) ? ValheimReporter.ConfigAPIKey.Value : 5__2, 5__3); <>1__state = -4; <>2__current = 5__9.SendWebRequest(); <>1__state = 1; return true; } <>m__Finally1(); <>s__1 = default(List.Enumerator); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>s__1).Dispose(); } private void <>m__Finally2() { <>1__state = -3; if (5__9 != null) { ((IDisposable)5__9).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public string Date { get; set; } public string Time { get; set; } public Texture2D Image1 { get; set; } public Texture2D Image2 { get; set; } public string PlayerName { get; set; } public string SteamID { get; set; } public string DiscordID { get; set; } public string Coordinates { get; set; } public List Webhook { get; set; } public string Category { get; set; } public string SubCategory { get; set; } public string Subject { get; set; } public string Description { get; set; } public string ReportID { get; private set; } public Report() { DateTime now = DateTime.Now; Date = now.ToString("yyyy-MM-dd"); Time = now.ToString("HH-mm-ss"); } public void GenerateReportID() { ReportID = "[" + PlayerName + "] - " + Date + "_" + Time; } private void SaveToLocalLogs() { try { string path = Date + "_" + Time; string text = Path.Combine(ValheimReporter.LogsPath, path); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string text2 = Path.Combine(text, Date + "_log.json"); var anon = new { ReportID, Date, Time, PlayerName, SteamID, DiscordID, Coordinates, Webhook, Category, SubCategory, Subject, Description }; string contents = JsonConvert.SerializeObject((object)anon, (Formatting)1); File.WriteAllText(text2, contents); if ((Object)(object)Image1 != (Object)null) { Utils.Screen.SaveTextureToFile(Image1, Path.Combine(text, "world_view")); } if ((Object)(object)Image2 != (Object)null) { Utils.Screen.SaveTextureToFile(Image2, Path.Combine(text, "map_view")); } Utils.LogSystem("Report", "SaveLocal", "Log saved to: " + text2, "success", 3); } catch (Exception ex) { Utils.LogSystem("Report", "SaveLocal", "Error saving log: " + ex.Message, "error", 1); } } [IteratorStateMachine(typeof(d__59))] public IEnumerator SendReportCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__59(0) { <>4__this = this }; } } public static class Utils { public static class Colors { public static class Text { public static readonly string RED = "\u001b[31m"; public static readonly string RED_BOLD = "\u001b[31;1m"; public static readonly string GREEN = "\u001b[32m"; public static readonly string GREEN_BOLD = "\u001b[32;1m"; public static readonly string YELLOW = "\u001b[33m"; public static readonly string YELLOW_BOLD = "\u001b[33;1m"; public static readonly string BLUE = "\u001b[34m"; public static readonly string BLUE_BOLD = "\u001b[34;1m"; public static readonly string MAGENTA = "\u001b[35m"; public static readonly string MAGENTA_BOLD = "\u001b[35;1m"; public static readonly string CYAN = "\u001b[36m"; public static readonly string CYAN_BOLD = "\u001b[36;1m"; public static readonly string WHITE = "\u001b[37m"; public static readonly string WHITE_BOLD = "\u001b[37;1m"; public static readonly string GRAY = "\u001b[90m"; public static readonly string GRAY_BOLD = "\u001b[90;1m"; public static readonly string BLACK = "\u001b[30m"; public static readonly string BLACK_BOLD = "\u001b[30;1m"; } public static class ForeGround { public static readonly string BLACK = "\u001b[40m"; public static readonly string RED = "\u001b[41m"; public static readonly string GREEN = "\u001b[42m"; public static readonly string YELLOW = "\u001b[43m"; public static readonly string BLUE = "\u001b[44m"; public static readonly string MAGENTA = "\u001b[45m"; public static readonly string CYAN = "\u001b[46m"; public static readonly string WHITE = "\u001b[47m"; } public static readonly string BOLD = "\u001b[1m"; public static readonly string UNDERLINED = "\u001b[4m"; public static readonly string RESET = "\u001b[0m"; } public static class PlayerUtils { private static Vector3 playerLastPosition = Vector3.zero; public static Vector3 GetPlayerPosition() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_001d: Unknown result type (might be due to invalid IL or missing references) playerLastPosition = ((Component)Player.m_localPlayer).transform.position; return playerLastPosition; } public static Vector3 GetPlayerLastPosition() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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) return playerLastPosition; } } public static class Screen { public static bool toggleCursorMode; public static bool preventPlayerMovement; public static void ToggleCursorMode(bool preventPlayerMovement = false) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (!(((Scene)(ref activeScene)).name != "start") || !((Object)(object)Player.m_localPlayer != (Object)null)) { return; } if (toggleCursorMode) { Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; toggleCursorMode = false; FieldInfo field = typeof(GameCamera).GetField("m_mouseCapture", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(GameCamera.instance, true); } } else { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; toggleCursorMode = true; Screen.preventPlayerMovement = preventPlayerMovement; } } public static void SaveTextureToFile(Texture2D tex, string fileName) { if (!((Object)(object)tex == (Object)null)) { byte[] bytes = null; switch (ValheimReporter.ConfigImageFormat.Value) { case ValheimReporter.ImageFormat.JPG: fileName += ".jpg"; bytes = ImageConversion.EncodeToJPG(tex, 75); break; case ValheimReporter.ImageFormat.PNG: fileName += ".png"; bytes = ImageConversion.EncodeToPNG(tex); break; } File.WriteAllBytes(fileName, bytes); } } } public static bool debugMode = false; public static bool outputMode = false; private static bool hasInitializedLogFile = false; public static bool isReporterOpen = false; public static readonly string ENCRIPTION_HEADER = "[NH]-3247"; private static readonly ManualLogSource Logger = Logger.CreateLogSource("ValheimReporter"); public static string GetVersion() { return typeof(ValheimReporter).Assembly.GetName().Version.ToString(); } public static void LogSystem(string sender, string function, string message, string type = "log", int level = 10, bool forcedVerbose = false) { if (!(debugMode || forcedVerbose) && level >= 4) { return; } string text = ""; string text2 = Colors.Text.WHITE_BOLD + " || "; string text3 = Colors.Text.WHITE_BOLD + " ->> "; string text4 = Colors.Text.CYAN; string text5 = Colors.Text.RED; string text6 = Colors.Text.MAGENTA_BOLD; string text7 = Colors.Text.YELLOW; string wHITE_BOLD = Colors.Text.WHITE_BOLD; string wHITE_BOLD2 = Colors.Text.WHITE_BOLD; string text8 = type.ToLower(); string text9 = text8; if (!(text9 == "success")) { if (text9 == "error") { text = Colors.ForeGround.RED; text4 = Colors.Text.WHITE_BOLD; text5 = Colors.Text.WHITE_BOLD; text6 = Colors.Text.WHITE_BOLD; text7 = Colors.Text.WHITE_BOLD; wHITE_BOLD = Colors.Text.WHITE_BOLD; wHITE_BOLD2 = Colors.Text.WHITE_BOLD; } } else { text = Colors.ForeGround.GREEN; text4 = Colors.Text.WHITE_BOLD; text5 = Colors.Text.WHITE_BOLD; text6 = Colors.Text.WHITE_BOLD; text7 = Colors.Text.WHITE_BOLD; wHITE_BOLD = Colors.Text.WHITE_BOLD; wHITE_BOLD2 = Colors.Text.WHITE_BOLD; } string text10 = text4 + "#### [NH]" + text2 + text5 + "[VALHEIM REPORTER]"; string text11 = text6 + "< " + sender + " >"; string text12 = text7 + "{ " + function + " }"; string text13 = wHITE_BOLD + "[ " + type.ToUpper() + " ]"; string text14 = wHITE_BOLD2 + message; string text15 = text + text10 + text2; if (debugMode || level == 1) { text15 = text15 + text11 + text2 + text12 + text2 + text13; } else { switch (level) { case 2: text15 = text15 + text11 + text2 + text13; break; case 3: text15 += text13; break; } } text15 = text15 + text3 + text14 + Colors.RESET; Logger.LogMessage((object)text15); if (!outputMode) { return; } text2 = " || "; text3 = " ->> "; text10 = "#### [NH]" + text2 + "[VALHEIM REPORTER]"; text11 = "< " + sender + " >"; text12 = "{ " + function + " }"; text13 = "[ " + type.ToUpper() + " ]"; text14 = text3 + message; text15 = text10 + text2 + text11 + text2 + text12 + text2 + text13 + text2 + text14; try { string path = Path.Combine(ValheimReporter.RootConfigValheimPath, "NH-ValheimReporter.log"); if (!hasInitializedLogFile) { File.WriteAllText(path, text15 + Environment.NewLine); hasInitializedLogFile = true; } else { File.AppendAllText(path, text15 + Environment.NewLine); } } catch (Exception ex) { LogSystem("ValheimReporter", "LogSystem", "Failed to write log file: " + ex.Message, "error"); } } public static void DeleteOldLogs() { try { if (!Directory.Exists(ValheimReporter.LogsPath)) { return; } DirectoryInfo directoryInfo = new DirectoryInfo(ValheimReporter.LogsPath); DirectoryInfo[] directories = directoryInfo.GetDirectories(); int value = ValheimReporter.ConfigMaxLogs.Value; if (directories.Length <= value) { return; } IEnumerable enumerable = directories.OrderBy((DirectoryInfo f) => f.Name).Take(directories.Length - value); foreach (DirectoryInfo item in enumerable) { try { item.Delete(recursive: true); LogSystem("LogManager", "DeleteOldLogs", "Deleted old log folder: " + item.Name, "log", 3); } catch (Exception ex) { LogSystem("LogManager", "DeleteOldLogs", "Failed to delete " + item.Name + ": " + ex.Message, "error", 1); } } } catch (Exception ex2) { LogSystem("LogManager", "DeleteOldLogs", "Critical error managing logs: " + ex2.Message, "error", 1); } } } [HarmonyPatch(typeof(GameCamera), "UpdateCamera")] public static class GameCamera_Patch { private static bool Prefix() { if (Utils.Screen.toggleCursorMode) { FieldInfo field = typeof(GameCamera).GetField("m_mouseCapture", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(GameCamera.instance, false); } return false; } return true; } } [HarmonyPatch(typeof(Player), "CanMove")] public static class Player_Movement { private static bool Prefix() { if (Utils.Screen.toggleCursorMode && Utils.Screen.preventPlayerMovement) { return false; } return true; } } [HarmonyPatch(typeof(Character), "Jump")] public static class Player_Jump { private static bool Prefix(Character __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && ((Character)val).IsPlayer() && Utils.Screen.toggleCursorMode && Utils.Screen.preventPlayerMovement) { return false; } return true; } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class BlockInputPatch { private static bool Prefix() { if (Utils.isReporterOpen) { return false; } return true; } } [BepInPlugin("n1h1lius.valheimreporter", "Valheim Reporter", "1.0.0")] public class ValheimReporter : BaseUnityPlugin { public enum ImageFormat { JPG, PNG } public static AssetBundle MainAssetBundle; public static CategoriesRoot ReportCategories; private static GameObject _instanciaUI; private static ValheimReporterController _controller; public static bool playSoundFlag = false; public static ConfigEntry ConfigOpenKey; public static ConfigEntry ConfigCloseOnSend; public static ConfigEntry ConfigFadeDuration; public static ConfigEntry ConfigRequirementsRemark; public static ConfigEntry ConfigConfirmationSound; public static ConfigEntry ConfigDiscordID; public static ConfigEntry ConfigAPIKey; public static ConfigEntry ConfigWorldName; public static ConfigEntry ConfigDeleteLogs; public static ConfigEntry ConfigMaxLogs; public static ConfigEntry ConfigMapZoom; public static ConfigEntry ConfigImageFormat; public static ConfigEntry ConfigDebugMode; public static ConfigEntry ConfigOutputMode; public static readonly string RootConfigValheimPath = Path.Combine(Paths.ConfigPath, "ValheimReporter"); public static readonly string DataPath = Path.Combine(RootConfigValheimPath, "Data"); public static readonly string AssetsPath = Path.Combine(DataPath, "Assets"); public static readonly string ImagesPath = Path.Combine(DataPath, "Images"); public static readonly string LogsPath = Path.Combine(RootConfigValheimPath, "Logs"); private readonly Harmony harmony = new Harmony("com.n1h1lius.valheimreporter"); private void Awake() { EnsurePluginFoldersAndFiles(); harmony.PatchAll(); Init(); } private void Update() { if (HandleOpenManager()) { if ((Object)(object)_instanciaUI == (Object)null) { Utils.LogSystem("ValheimReporter", "Update", "INIT UI...", "log", 3); GameObject val = MainAssetBundle.LoadAsset("ReporterCanvas"); _instanciaUI = Object.Instantiate(val); Object.DontDestroyOnLoad((Object)(object)_instanciaUI); _controller = _instanciaUI.AddComponent(); _controller.StartReporter(); } if ((Object)(object)_controller != (Object)null) { Utils.LogSystem("ValheimReporter", "Update", "Toggle Main Manager...", "log", 4); _controller.ToggleMainManager(); } } if ((Object)(object)_instanciaUI != (Object)null && (Object)(object)_controller != (Object)null && _controller.getIsReporterOpen()) { _controller.UpdateFooterInfo(); } } private void OnDestroy() { harmony.UnpatchSelf(); } private void Init() { string text = Path.Combine(AssetsPath, "valheim_reporter_bundle"); if (File.Exists(text)) { MainAssetBundle = AssetBundle.LoadFromFile(text); Utils.LogSystem("ValheimReporter", "Awake", "Bundle Loaded Successfully.", "success", 3); } else { Utils.LogSystem("ValheimReporter", "Awake", "Bundle Not Found.", "error", 1); } ReportCategories = CategoryLoader.Load("categories.json"); if (ReportCategories != null) { Utils.LogSystem("ValheimReporter", "Awake", "Categories Loaded Successfully.", "success", 3); } ConfigOpenKey = ((BaseUnityPlugin)this).Config.Bind("General", "Open Key", (KeyCode)291, "The key to open the main manager. Default: F10"); ConfigCloseOnSend = ((BaseUnityPlugin)this).Config.Bind("General", "Close On Send", true, "Close the main manager when a report is sent. Default: true"); ConfigFadeDuration = ((BaseUnityPlugin)this).Config.Bind("General", "Fade Duration", 0.09f, "The duration of the fade animation of the main manager. Default: 0.09"); ConfigRequirementsRemark = ((BaseUnityPlugin)this).Config.Bind("General", "Requirements Remark", true, "Show the requirements remark in Report Screen. It's just a visual aid to notice when some parameters are missing. Default: true"); ConfigConfirmationSound = ((BaseUnityPlugin)this).Config.Bind("General", "Confirmation Sound", true, "Play a confirmation sound when a report is sent. Default: true"); ConfigDiscordID = ((BaseUnityPlugin)this).Config.Bind("ServerRequirements", "Discord ID", "Discord ID", "Your Discord ID. Default: Discord ID"); ConfigAPIKey = ((BaseUnityPlugin)this).Config.Bind("ServerRequirements", "API Key", "API_KEY", "Server's API Key. Default: API_KEY"); ConfigWorldName = ((BaseUnityPlugin)this).Config.Bind("ServerRequirements", "World Name", "WORLD_NAME", "Server's World Name. Default: WORLD_NAME"); ConfigMapZoom = ((BaseUnityPlugin)this).Config.Bind("Screenshots", "Map Zoom", 0.5f, "The zoom level of the map on the screenshots. 0.5f - 1.0f is a balanced zoom for seeing the area. // 0.5f is a good value. The smaller the number, the closer (0.1 very close, 1.0 very far). Default: 0.5"); ConfigImageFormat = ((BaseUnityPlugin)this).Config.Bind("Screenshots", "Image Format", ImageFormat.JPG, "The format of the screenshots ouput. This is just client side. Reports will always send JPG high compression images. Default: JPG"); ConfigDeleteLogs = ((BaseUnityPlugin)this).Config.Bind("LogSystem", "Delete Reports", false, "Delete the report files when the maximum number of reports is reached. Default: false"); ConfigMaxLogs = ((BaseUnityPlugin)this).Config.Bind("LogSystem", "Max Logs", 10, "The maximum number of report files to keep. Default: 10"); ConfigOutputMode = ((BaseUnityPlugin)this).Config.Bind("LogSystem", "Output Mode", false, "Enable output mode. It will output the logs a .log file. Default: false"); ConfigDebugMode = ((BaseUnityPlugin)this).Config.Bind("DebugMode", "Debug Mode", false, "Enable debug mode. For developers only. Default: false"); Utils.debugMode = ConfigDebugMode.Value; Utils.outputMode = ConfigOutputMode.Value; if (ConfigDeleteLogs.Value && ConfigMaxLogs.Value > 0) { Utils.DeleteOldLogs(); } Utils.LogSystem("ValheimReporter", "Awake", "Plugin Loaded Successfully.", "success", 3); } private bool HandleOpenManager() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } if (Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible()) { return false; } if (Game.IsPaused()) { return false; } if (Input.GetKeyDown(ConfigOpenKey.Value)) { return true; } return false; } public static GameObject GetInstance() { return _instanciaUI; } private void EnsurePluginFoldersAndFiles() { try { if (!Directory.Exists(AssetsPath)) { Directory.CreateDirectory(AssetsPath); } if (!Directory.Exists(ImagesPath)) { Directory.CreateDirectory(ImagesPath); } if (!Directory.Exists(LogsPath)) { Directory.CreateDirectory(LogsPath); } Dictionary dictionary = new Dictionary { { Path.Combine(AssetsPath, "confirm.ogg"), "ValheimReporter.ValheimReporter.Data.Assets.confirm.ogg" }, { Path.Combine(AssetsPath, "valheim_reporter_bundle"), "ValheimReporter.ValheimReporter.Data.Assets.valheim_reporter_bundle" }, { Path.Combine(ImagesPath, "NoPicture.png"), "ValheimReporter.ValheimReporter.Data.Images.NoPicture.png" }, { Path.Combine(DataPath, "samples_categories.json"), "ValheimReporter.ValheimReporter.Data.samples_categories.json" }, { Path.Combine(DataPath, "categories.json"), "ValheimReporter.ValheimReporter.Data.categories.json" } }; foreach (KeyValuePair item in dictionary) { string key = item.Key; string value = item.Value; if (!File.Exists(key)) { Utils.LogSystem("ValheimReporter", "EnsurePluginFoldersAndFiles", "Extracting missing file: " + Path.GetFileName(key), "success", 1); ExtractEmbeddedResource(value, key); } } } catch (Exception ex) { Utils.LogSystem("ValheimReporter", "EnsurePluginFoldersAndFiles", "Error during folder/file setup: " + ex.Message, "error", 1); } } private void ExtractEmbeddedResource(string resourceName, string outputPath) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using Stream stream = executingAssembly.GetManifestResourceStream(resourceName); if (stream == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Resource not found: " + resourceName)); return; } using FileStream destination = new FileStream(outputPath, FileMode.Create, FileAccess.Write); stream.CopyTo(destination); } } public class ValheimReporterController : MonoBehaviour { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__59_10; public static UnityAction <>9__59_11; internal void b__59_10() { Application.OpenURL("https://github.com/n1h1lius"); } internal void b__59_11() { Application.OpenURL("https://github.com/n1h1lius/Valheim-Reporter"); } } [CompilerGenerated] private sealed class d__76 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ValheimReporterController <>4__this; private bool 5__1; private bool 5__2; private FieldInfo 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__76(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Invalid comparison between Unknown and I4 //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = (Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_rootObject != (Object)null && Hud.instance.m_rootObject.activeSelf; if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_rootObject != (Object)null) { Hud.instance.m_rootObject.SetActive(false); } if ((Object)(object)<>4__this.mainCanvas != (Object)null) { ((Behaviour)<>4__this.mainCanvas).enabled = false; } <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: <>1__state = -1; <>4__this.screenshotImage1.texture = (Texture)(object)ScreenCapture.CaptureScreenshotAsTexture(); if ((Object)(object)Minimap.instance != (Object)null) { Hud.instance.m_rootObject.SetActive(true); 5__2 = (int)Minimap.instance.m_mode == 2; Minimap.instance.SetMapMode((MapMode)2); 5__3 = typeof(Minimap).GetField("m_largeZoom", BindingFlags.Instance | BindingFlags.NonPublic); if (5__3 != null) { 5__3.SetValue(Minimap.instance, ValheimReporter.ConfigMapZoom.Value); } <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 2; return true; } break; case 2: <>1__state = -1; <>4__this.screenshotImage2.texture = (Texture)(object)ScreenCapture.CaptureScreenshotAsTexture(); if (!5__2) { Minimap.instance.SetMapMode((MapMode)1); } 5__3 = null; break; } <>4__this.screenshotTaken = true; if ((Object)(object)<>4__this.mainCanvas != (Object)null) { ((Behaviour)<>4__this.mainCanvas).enabled = true; } if (((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_rootObject != (Object)null) & 5__1) { Hud.instance.m_rootObject.SetActive(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(); } } [CompilerGenerated] private sealed class d__67 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CanvasGroup group; public float startAlpha; public float endAlpha; public Action onComplete; public ValheimReporterController <>4__this; private float 5__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__67(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = 0f; group.alpha = startAlpha; if (endAlpha > 0f) { group.blocksRaycasts = true; group.interactable = true; } else { group.blocksRaycasts = false; group.interactable = false; } break; case 1: <>1__state = -1; break; } if (5__1 < <>4__this.fadeDuration) { 5__1 += Time.unscaledDeltaTime; group.alpha = Mathf.Lerp(startAlpha, endAlpha, 5__1 / <>4__this.fadeDuration); <>2__current = null; <>1__state = 1; return true; } group.alpha = endAlpha; onComplete?.Invoke(); 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 d__74 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ValheimReporterController <>4__this; private UnityWebRequest 5__1; private string 5__2; private string 5__3; private string[] 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__74(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = UnityWebRequest.Get(<>4__this.apiUrl); <>1__state = -3; <>2__current = 5__1.SendWebRequest(); <>1__state = 1; return true; case 1: <>1__state = -3; if ((int)5__1.result == 2 || (int)5__1.result == 3) { Debug.LogError((object)("FETCHING API ERROR: " + 5__1.error)); } else { 5__2 = 5__1.downloadHandler.text; 5__3 = 5__2.Trim('[', ']'); 5__4 = 5__3.Split(new string[1] { "\",\"" }, StringSplitOptions.None); if (5__4.Length >= 2) { ((TMP_Text)<>4__this.stanzaText).text = 5__4[0].Replace("\"", ""); ((TMP_Text)<>4__this.referenceText).text = "Hávamál - " + 5__4[1].Replace("\"", ""); } 5__2 = null; 5__3 = null; 5__4 = null; } <>m__Finally1(); 5__1 = null; return false; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (5__1 != null) { ((IDisposable)5__1).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__83 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string path; public ValheimReporterController <>4__this; private string 5__1; private UnityWebRequest 5__2; private AudioClip 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__83(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__1 = null; 5__2 = null; 5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Invalid comparison between Unknown and I4 //IL_00c5: 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) try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = "file://" + path; 5__2 = UnityWebRequestMultimedia.GetAudioClip(5__1, (AudioType)14); <>1__state = -3; <>2__current = 5__2.SendWebRequest(); <>1__state = 1; return true; case 1: <>1__state = -3; if ((int)5__2.result == 1) { 5__3 = DownloadHandlerAudioClip.GetContent(5__2); if ((Object)(object)Player.m_localPlayer != (Object)null) { AudioSource.PlayClipAtPoint(5__3, ((Component)Player.m_localPlayer).transform.position, 1f); Utils.LogSystem("ValheimReporterController", "LoadAndPlay", $"Audio played at: {((Component)Player.m_localPlayer).transform.position}", "log", 4); } 5__3 = null; } else { Utils.LogSystem("Audio", "Error", "Cannot load audio: " + 5__2.error, "error", 1); } <>m__Finally1(); 5__2 = null; return false; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (5__2 != null) { ((IDisposable)5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__66 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Canvas nextCanvas; public CanvasGroup nextGroup; public string mode; public ValheimReporterController <>4__this; private string <>s__1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__66(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>s__1 = null; <>1__state = -2; } private bool MoveNext() { string text; switch (<>1__state) { default: return false; case 0: <>1__state = -1; if ((Object)(object)<>4__this.currentActiveSubModal != (Object)null && ((Component)<>4__this.currentActiveSubModal).gameObject.activeSelf) { <>2__current = ((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.Fade(<>4__this.currentActiveSubModalGroup, 1f, 0f)); <>1__state = 1; return true; } goto IL_00b8; case 1: <>1__state = -1; ((Component)<>4__this.currentActiveSubModal).gameObject.SetActive(false); goto IL_00b8; case 2: { <>1__state = -1; <>4__this.currentActiveSubModal = nextCanvas; <>4__this.currentActiveSubModalGroup = nextGroup; return false; } IL_00b8: text = mode; <>s__1 = text; switch (<>s__1) { case "reporter": <>4__this.isReporterOpen = true; <>4__this.HandleDropdown(<>4__this.reportTypeDropdown, "type"); <>4__this.HandleDropdown(<>4__this.reportSubTypeDropdown, "subtype"); break; case "help": <>4__this.isReporterOpen = false; <>4__this.stanzaText = <>4__this.helpStanzaText; <>4__this.referenceText = <>4__this.helpStanzaReference; break; case "about": <>4__this.isReporterOpen = false; <>4__this.stanzaText = <>4__this.aboutStanzaText; <>4__this.referenceText = <>4__this.aboutStanzaReference; break; } <>s__1 = null; ((Component)nextCanvas).gameObject.SetActive(true); if (<>4__this.finishedReport) { <>4__this.ResetReporterModal(); } if (mode != "reporter") { <>4__this.RequestNewStanza(); } <>2__current = ((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.Fade(nextGroup, 0f, 1f)); <>1__state = 2; return true; } } 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("Status")] private bool isReporterOpen = false; private bool isMainReporterOpen = false; [Header("Main Settings")] private float fadeDuration = ValheimReporter.ConfigFadeDuration.Value; private Canvas mainCanvas; private CanvasGroup mainCanvasGroup; [Header("Navigation Buttons")] private Button openReportsModalButton; private Button helpButton; private Button aboutButton; [Header("Reports Modal")] private Canvas reportsModalCanvas; private CanvasGroup reportsModalCanvasGroup; private TextMeshProUGUI modalSectionTitle; private TextMeshProUGUI modalSectionType; private TextMeshProUGUI modalSectionSubType; [Header("Reports Body Content")] private Canvas reportsModalBodyCanvas; private Button generateScreenshotsButton; private Button submitReportButton; private RawImage screenshotImage1; private RawImage screenshotImage2; private TMP_Dropdown reportTypeDropdown; private TMP_Dropdown reportSubTypeDropdown; private TMP_InputField reportAboutInput; private TMP_InputField reportContentInput; [Header("Help Modal")] private Canvas helpModalCanvas; private CanvasGroup helpModalCanvasGroup; private TextMeshProUGUI helpStanzaText; private TextMeshProUGUI helpStanzaReference; [Header("About Modal")] private Canvas aboutModalCanvas; private CanvasGroup aboutModalCanvasGroup; private TextMeshProUGUI aboutStanzaText; private TextMeshProUGUI aboutStanzaReference; private Button checkMyGithubButton; private Button checkModGithubButton; [Header("Footer Info")] private TextMeshProUGUI footerDateLabel; private TextMeshProUGUI footerTimeLabel; private TextMeshProUGUI footerCoorLabel; [Header("Transition Tracking")] private Canvas currentActiveSubModal = null; private CanvasGroup currentActiveSubModalGroup = null; [Header("Warning Modal")] private Canvas warningModalCanvas; private CanvasGroup warningModalCanvasGroup; private TextMeshProUGUI warningTextReason; private Button warningAcceptButton; private TextMeshProUGUI stanzaText; private TextMeshProUGUI referenceText; private Texture2D defaultTexture; private Category currentCategory; private Subcategory currentSubcategory; private bool aboutIsRequired = false; private bool aboutIsRequiredAnimationFlag = false; private bool contentIsRequiredAnimationFlag = false; private bool pulsatingIsRequired = false; private int stoppedPulsating = 0; private bool contentIsRequired = false; public bool finishedReport = false; public bool screenshotTaken = false; private string apiUrl = "https://odin-api.orlog.workers.dev/havamal/english/random"; private void Awake() { mainCanvasGroup = ((Component)this).GetComponent(); } private void Update() { if (ValheimReporter.ConfigRequirementsRemark.Value) { if (aboutIsRequired) { RequiredAnimation(reportAboutInput, ref aboutIsRequiredAnimationFlag); } if (contentIsRequired) { RequiredAnimation(reportContentInput, ref contentIsRequiredAnimationFlag); } if (stoppedPulsating < 2) { RequiredImageAnimation(screenshotImage1, pulsatingIsRequired); RequiredImageAnimation(screenshotImage2, pulsatingIsRequired); } } if (ValheimReporter.ConfigConfirmationSound.Value && ValheimReporter.playSoundFlag) { PlayConfirmSound(); ValheimReporter.playSoundFlag = false; if (ValheimReporter.ConfigCloseOnSend.Value) { ToggleMainManager(); } } } public void StartReporter() { Utils.LogSystem("ValheimReporterController", "StartReporter", "Initializing Valheim Reporter...", "log", 4); InitializeReferences(); BindUIEvents(); SetupInitialState(); ((Component)mainCanvas).gameObject.SetActive(false); Utils.LogSystem("ValheimReporterController", "StartReporter", "Valheim Reporter initialized.", "success", 3); } private void InitializeReferences() { Utils.LogSystem("ValheimReporterController", "InitializeReferences", "Initializing Valheim Reporter references...", "log", 4); mainCanvas = ((Component)this).GetComponent(); Utils.LogSystem("ValheimReporterController", "InitializeReferences", "Initializing Navigation references...", "log", 4); openReportsModalButton = GameObject.Find("ReporterFrameOpenReportsModalButton").GetComponent