Please disclose if your mod was created primarily 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 HeimdallVoice v0.1.0
HeimdallVoice.dll
Decompiled 10 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; 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: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("HeimdallVoice")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6038451d89c7bb0324241a2c518aea9778756540")] [assembly: AssemblyProduct("HeimdallVoice")] [assembly: AssemblyTitle("HeimdallVoice")] [assembly: AssemblyVersion("1.0.0.0")] namespace HeimdallVoice; public static class ApiClient { private static string _baseUrl = "http://127.0.0.1:24600"; private static readonly HttpClient Http = new HttpClient { Timeout = TimeSpan.FromSeconds(3.0) }; public static void Configure(string baseUrl) { _baseUrl = PluginSettings.NormalizeBaseUrl(baseUrl); ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogInfo((object)("Bridge configurado para " + _baseUrl)); } } public static void SendZoneUpdate(string steamId, ZoneData zone) { SendRequestAsync(_baseUrl + "/move", steamId, zone?.name, null, zone?.discordChannelId); } public static void RestorePreviousChannel(string steamId) { SendRequestAsync(_baseUrl + "/restore", steamId, null); } public static void RegisterLinkCode(string steamId, string code) { SendRequestAsync(_baseUrl + "/register-link", steamId, null, code); } private static async Task SendRequestAsync(string url, string steamId, string zone, string code = null, string discordChannelId = null) { try { var payload = new { steamId, zone, code, discordChannelId }; string json = JsonConvert.SerializeObject((object)payload); StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); try { HttpResponseMessage response = await Http.PostAsync(url, (HttpContent)(object)content).ConfigureAwait(continueOnCapturedContext: false); if (!response.IsSuccessStatusCode) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)$"Erro ao chamar API ({url}): {(int)response.StatusCode} {response.ReasonPhrase}"); } return; } } finally { ((IDisposable)content)?.Dispose(); } ManualLogSource log2 = HeimdallVoicePlugin.Log; if (log2 != null) { log2.LogInfo((object)("API chamada com sucesso (" + url + ")")); } } catch (Exception ex2) { Exception ex = ex2; ManualLogSource log3 = HeimdallVoicePlugin.Log; if (log3 != null) { log3.LogError((object)("Falha ao chamar API (" + url + "): " + ex.Message)); } } } } [BepInPlugin("heimdall.voice", "HeimdallVoice", "0.4.2")] public class HeimdallVoicePlugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <ShowExtendedMessage>d__17 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public string message; public HeimdallVoicePlugin <>4__this; private int <i>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ShowExtendedMessage>d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <i>5__1 = 0; break; case 1: <>1__state = -1; <i>5__1++; break; } if (<i>5__1 < 3) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } ((Character)Player.m_localPlayer).Message((MessageType)1, message, 0, (Sprite)null); <>2__current = (object)new WaitForSeconds(1.8f); <>1__state = 1; return 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(); } } public static ManualLogSource Log; public static HeimdallVoicePlugin Instance; private ZoneManager _zoneManager; private ZoneData _currentZone; private float _checkTimer; private LinkCodeManager _linkCodeManager; private Harmony _harmony; private string _clientId; private PluginSettings _settings; private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"HeimdallVoice initializing..."); _linkCodeManager = new LinkCodeManager(); _settings = PluginSettings.Bind(((BaseUnityPlugin)this).Config); _zoneManager = new ZoneManager(((BaseUnityPlugin)this).Config); _zoneManager.LoadZones(); ApiClient.Configure(_settings.BridgeBaseUrl); _clientId = LoadOrCreateClientId(); RegisterCommands(); PatchChatVoiceCommand(); Log.LogInfo((object)"HeimdallVoice initialized."); } private void RegisterCommands() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("voice", "Gera codigo de vinculo do Discord", (ConsoleEvent)delegate { HandleVoiceCommand(); }, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); Log.LogInfo((object)"Comando 'voice' registrado."); } private void PatchChatVoiceCommand() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("Chat"); if (type == null) { Log.LogWarning((object)"Tipo Chat nao encontrado; /voice no chat nao foi habilitado."); return; } MethodInfo methodInfo = AccessTools.Method(type, "InputText", (Type[])null, (Type[])null); if (methodInfo == null) { Log.LogWarning((object)"Metodo Chat.InputText nao encontrado; /voice no chat nao foi habilitado."); return; } _harmony = new Harmony("heimdall.voice.chat"); HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(HeimdallVoicePlugin), "ChatInputTextPrefix", (Type[])null, (Type[])null)); _harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Comando /voice no chat habilitado."); } catch (Exception ex) { Log.LogError((object)("Falha ao habilitar /voice no chat: " + ex.Message)); } } private static bool ChatInputTextPrefix(object __instance) { try { if ((Object)(object)Instance == (Object)null) { return true; } string chatInputText = Instance.GetChatInputText(__instance); if (string.IsNullOrWhiteSpace(chatInputText)) { return true; } string text = chatInputText.Trim(); if (!text.Equals("/voice", StringComparison.OrdinalIgnoreCase)) { return true; } Instance.HandleVoiceCommand(); Instance.ClearChatInputText(__instance); return false; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Erro no interceptador de /voice: " + ex.Message)); } return true; } } private string GetChatInputText(object chatInstance) { if (chatInstance == null) { return null; } Type type = chatInstance.GetType(); FieldInfo fieldInfo = AccessTools.Field(type, "m_input"); if (fieldInfo == null) { return null; } object value = fieldInfo.GetValue(chatInstance); if (value == null) { return null; } PropertyInfo propertyInfo = AccessTools.Property(value.GetType(), "text"); if (propertyInfo != null) { return propertyInfo.GetValue(value, null) as string; } FieldInfo fieldInfo2 = AccessTools.Field(value.GetType(), "text"); if (fieldInfo2 != null) { return fieldInfo2.GetValue(value) as string; } return null; } private void ClearChatInputText(object chatInstance) { if (chatInstance == null) { return; } Type type = chatInstance.GetType(); object obj = AccessTools.Field(type, "m_input")?.GetValue(chatInstance); if (obj != null) { PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "text"); if (propertyInfo != null) { propertyInfo.SetValue(obj, string.Empty, null); } (AccessTools.Method(type, "Hide", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "HideInput", (Type[])null, (Type[])null))?.Invoke(chatInstance, null); } } private void HandleVoiceCommand() { if (string.IsNullOrEmpty(_clientId)) { ShowInGame("[HeimdallVoice] ClientID invalido.", extendedTime: false); return; } string text = _linkCodeManager.GenerateCode(_clientId); ApiClient.RegisterLinkCode(_clientId, text); string message = "[HeimdallVoice] Codigo: " + text + ". Use no Discord: !link " + text; ShowInGame(message, extendedTime: true); Log.LogInfo((object)("Codigo de vinculo gerado para " + _clientId + ": " + text)); } private void ShowInGame(string message, bool extendedTime) { Log.LogInfo((object)message); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (!extendedTime) { ((Character)Player.m_localPlayer).Message((MessageType)1, message, 0, (Sprite)null); } else { ((MonoBehaviour)this).StartCoroutine(ShowExtendedMessage(message)); } } } [IteratorStateMachine(typeof(<ShowExtendedMessage>d__17))] private IEnumerator ShowExtendedMessage(string message) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ShowExtendedMessage>d__17(0) { <>4__this = this, message = message }; } private void Update() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } _checkTimer += Time.deltaTime; if (_checkTimer < 1f) { return; } _checkTimer = 0f; if (string.IsNullOrEmpty(_clientId)) { Log.LogWarning((object)"ClientID invalido."); return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; ZoneData zoneForPosition = _zoneManager.GetZoneForPosition(position); if (zoneForPosition == null) { if (_currentZone != null) { Log.LogInfo((object)("Player saiu da zona " + _currentZone.name + "; restaurando canal anterior.")); _currentZone = null; ApiClient.RestorePreviousChannel(_clientId); } } else if (_currentZone == null || !(_currentZone.name == zoneForPosition.name)) { _currentZone = zoneForPosition; Log.LogInfo((object)("Player moved to zone " + zoneForPosition.name)); ApiClient.SendZoneUpdate(_clientId, zoneForPosition); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private string LoadOrCreateClientId() { try { string text = SystemInfo.deviceUniqueIdentifier?.Trim(); if (IsUsableDeviceId(text)) { string text2 = HashClientId(text); Log.LogInfo((object)("ClientID derivado do dispositivo: " + text2)); return text2; } string text3 = Path.Combine(Paths.ConfigPath, "HeimdallVoice"); string path = Path.Combine(text3, "client_id.txt"); if (File.Exists(path)) { string text4 = File.ReadAllText(path).Trim(); if (!string.IsNullOrEmpty(text4)) { Log.LogWarning((object)("ClientID em fallback local: " + text4)); return text4; } } Directory.CreateDirectory(text3); string text5 = Guid.NewGuid().ToString("N"); File.WriteAllText(path, text5); Log.LogWarning((object)("DeviceUniqueIdentifier indisponivel; fallback gerado: " + text5)); return text5; } catch (Exception ex) { Log.LogError((object)("Falha ao carregar/gerar ClientID: " + ex.Message)); return null; } } private static bool IsUsableDeviceId(string deviceId) { if (string.IsNullOrWhiteSpace(deviceId)) { return false; } return !deviceId.Equals("n/a", StringComparison.OrdinalIgnoreCase); } private static string HashClientId(string value) { using SHA256 sHA = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(value); byte[] array = sHA.ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } } public class LinkCodeManager { private Dictionary<string, string> _activeCodes = new Dictionary<string, string>(); private Random _random = new Random(); public string GenerateCode(string steamId) { string text = _random.Next(100000, 999999).ToString(); _activeCodes[steamId] = text; return text; } public string GetCode(string steamId) { if (_activeCodes.ContainsKey(steamId)) { return _activeCodes[steamId]; } return null; } } public class LinkManager { private Dictionary<string, string> _links; private string _filePath; public LinkManager() { _filePath = Path.Combine(Paths.ConfigPath, "HeimdallVoice", "links.json"); Load(); } private void Load() { if (!File.Exists(_filePath)) { _links = new Dictionary<string, string>(); return; } string text = File.ReadAllText(_filePath); _links = JsonConvert.DeserializeObject<Dictionary<string, string>>(text); } public string GetDiscordId(string steamId) { if (_links.ContainsKey(steamId)) { return _links[steamId]; } return null; } } public class PluginSettings { private class LegacyPluginSettings { public string bridgeBaseUrl = "http://127.0.0.1:24600"; } private class BridgeConfig { public string bridgeBaseUrl = "http://127.0.0.1:24600"; public string discordToken = "COLOQUE_O_TOKEN_DO_BOT"; public string guildId = "COLOQUE_O_ID_DO_SERVIDOR"; public string serverHost = "0.0.0.0"; public int serverPort = 24600; } private const string DefaultBridgeBaseUrl = "http://127.0.0.1:24600"; private const string DefaultDiscordToken = "COLOQUE_O_TOKEN_DO_BOT"; private const string DefaultGuildId = "COLOQUE_O_ID_DO_SERVIDOR"; private const string DefaultServerHost = "0.0.0.0"; private const int DefaultServerPort = 24600; private ConfigEntry<string> _bridgeBaseUrl; private ConfigEntry<string> _discordToken; private ConfigEntry<string> _guildId; private ConfigEntry<string> _serverHost; private ConfigEntry<int> _serverPort; private string _bridgeConfigPath; private bool _isSynchronizing; public string BridgeBaseUrl => NormalizeBaseUrl(_bridgeBaseUrl?.Value); public static PluginSettings Bind(ConfigFile config) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown PluginSettings pluginSettings = new PluginSettings(); string text = LoadLegacyBridgeBaseUrl(); BridgeConfig bridgeConfig = LoadBridgeConfig(); pluginSettings._bridgeConfigPath = ResolveBridgeConfigPath(); pluginSettings._bridgeBaseUrl = config.Bind<string>("Bridge", "URL Base", string.IsNullOrWhiteSpace(text) ? NormalizeBaseUrl(bridgeConfig.bridgeBaseUrl) : text, new ConfigDescription("Endereco base da API do HeimdallVoice Bridge.", (AcceptableValueBase)null, Array.Empty<object>())); pluginSettings._discordToken = config.Bind<string>("Bot do Discord", "Token do Bot", NormalizeText(bridgeConfig.discordToken, "COLOQUE_O_TOKEN_DO_BOT"), new ConfigDescription("Token do bot do Discord usado pelo HeimdallVoice Bridge.", (AcceptableValueBase)null, Array.Empty<object>())); pluginSettings._guildId = config.Bind<string>("Servidor do Discord", "ID do Servidor", NormalizeText(bridgeConfig.guildId, "COLOQUE_O_ID_DO_SERVIDOR"), new ConfigDescription("ID do servidor (guild) do Discord.", (AcceptableValueBase)null, Array.Empty<object>())); pluginSettings._serverHost = config.Bind<string>("Servidor da Bridge", "Host", NormalizeText(bridgeConfig.serverHost, "0.0.0.0"), new ConfigDescription("Host onde o HeimdallVoice Bridge vai escutar.", (AcceptableValueBase)null, Array.Empty<object>())); pluginSettings._serverPort = config.Bind<int>("Servidor da Bridge", "Porta", NormalizePort(bridgeConfig.serverPort), new ConfigDescription("Porta onde o HeimdallVoice Bridge vai escutar.", (AcceptableValueBase)null, Array.Empty<object>())); pluginSettings._bridgeBaseUrl.Value = NormalizeBaseUrl(pluginSettings._bridgeBaseUrl.Value); pluginSettings._discordToken.Value = NormalizeText(pluginSettings._discordToken.Value, "COLOQUE_O_TOKEN_DO_BOT"); pluginSettings._guildId.Value = NormalizeText(pluginSettings._guildId.Value, "COLOQUE_O_ID_DO_SERVIDOR"); pluginSettings._serverHost.Value = NormalizeText(pluginSettings._serverHost.Value, "0.0.0.0"); pluginSettings._serverPort.Value = NormalizePort(pluginSettings._serverPort.Value); pluginSettings._bridgeBaseUrl.SettingChanged += pluginSettings.HandleBridgeSettingChanged; pluginSettings._discordToken.SettingChanged += pluginSettings.HandleBridgeSettingChanged; pluginSettings._guildId.SettingChanged += pluginSettings.HandleBridgeSettingChanged; pluginSettings._serverHost.SettingChanged += pluginSettings.HandleBridgeSettingChanged; pluginSettings._serverPort.SettingChanged += pluginSettings.HandleBridgeSettingChanged; pluginSettings.PersistBridgeConfig(); return pluginSettings; } private static string LoadLegacyBridgeBaseUrl() { string path = Path.Combine(Paths.ConfigPath, "HeimdallVoice"); string path2 = Path.Combine(path, "settings.json"); try { if (!File.Exists(path2)) { return null; } string text = File.ReadAllText(path2); return NormalizeBaseUrl(JsonConvert.DeserializeObject<LegacyPluginSettings>(text)?.bridgeBaseUrl); } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)("Falha ao carregar settings.json legado: " + ex.Message)); } return null; } } public static string NormalizeBaseUrl(string url) { if (string.IsNullOrWhiteSpace(url)) { return "http://127.0.0.1:24600"; } return url.Trim().TrimEnd(new char[1] { '/' }); } private void HandleBridgeSettingChanged(object sender, EventArgs args) { if (_isSynchronizing) { return; } _isSynchronizing = true; try { _bridgeBaseUrl.Value = NormalizeBaseUrl(_bridgeBaseUrl.Value); _discordToken.Value = NormalizeText(_discordToken.Value, "COLOQUE_O_TOKEN_DO_BOT"); _guildId.Value = NormalizeText(_guildId.Value, "COLOQUE_O_ID_DO_SERVIDOR"); _serverHost.Value = NormalizeText(_serverHost.Value, "0.0.0.0"); _serverPort.Value = NormalizePort(_serverPort.Value); ApiClient.Configure(_bridgeBaseUrl.Value); PersistBridgeConfig(); } finally { _isSynchronizing = false; } } private void PersistBridgeConfig() { try { if (!string.IsNullOrWhiteSpace(_bridgeConfigPath)) { string directoryName = Path.GetDirectoryName(_bridgeConfigPath); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } BridgeConfig bridgeConfig = new BridgeConfig { bridgeBaseUrl = NormalizeBaseUrl(_bridgeBaseUrl.Value), discordToken = NormalizeText(_discordToken.Value, "COLOQUE_O_TOKEN_DO_BOT"), guildId = NormalizeText(_guildId.Value, "COLOQUE_O_ID_DO_SERVIDOR"), serverHost = NormalizeText(_serverHost.Value, "0.0.0.0"), serverPort = NormalizePort(_serverPort.Value) }; string contents = JsonConvert.SerializeObject((object)bridgeConfig, (Formatting)1); File.WriteAllText(_bridgeConfigPath, contents); } } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)("Falha ao salvar config do bridge: " + ex.Message)); } } } private static BridgeConfig LoadBridgeConfig() { string text = ResolveBridgeConfigPath(); BridgeConfig result = new BridgeConfig(); try { if (string.IsNullOrWhiteSpace(text) || !File.Exists(text)) { return result; } string text2 = File.ReadAllText(text); if (string.IsNullOrWhiteSpace(text2)) { return result; } BridgeConfig bridgeConfig = JsonConvert.DeserializeObject<BridgeConfig>(text2) ?? new BridgeConfig(); bridgeConfig.bridgeBaseUrl = NormalizeBaseUrl(bridgeConfig.bridgeBaseUrl); bridgeConfig.discordToken = NormalizeText(bridgeConfig.discordToken, "COLOQUE_O_TOKEN_DO_BOT"); bridgeConfig.guildId = NormalizeText(bridgeConfig.guildId, "COLOQUE_O_ID_DO_SERVIDOR"); bridgeConfig.serverHost = NormalizeText(bridgeConfig.serverHost, "0.0.0.0"); bridgeConfig.serverPort = NormalizePort(bridgeConfig.serverPort); return bridgeConfig; } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)("Falha ao carregar config do bridge: " + ex.Message)); } return result; } } private static string ResolveBridgeConfigPath() { try { string text = Directory.GetParent(Paths.BepInExRootPath)?.FullName; if (!string.IsNullOrWhiteSpace(text)) { return Path.Combine(text, "HeimdallVoice-Bridge", "config.json"); } } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogWarning((object)("Nao foi possivel resolver o caminho do bridge: " + ex.Message)); } } return Path.Combine(Paths.ConfigPath, "HeimdallVoice", "bridge-config.json"); } private static string NormalizeText(string value, string fallback) { return string.IsNullOrWhiteSpace(value) ? fallback : value.Trim(); } private static int NormalizePort(int port) { return (port <= 0) ? 24600 : port; } } public class ZoneManager { private const int MaxZoneSlots = 10; private List<ZoneData> _zones = new List<ZoneData>(); private string _zoneFilePath; private ConfigEntry<int> _zoneCount; private readonly List<ZoneConfigEntrySet> _zoneEntries = new List<ZoneConfigEntrySet>(); private bool _isSynchronizing; public ZoneManager(ConfigFile config) { _zoneFilePath = Path.Combine(Paths.ConfigPath, "HeimdallVoice", "zones.json"); BindConfig(config); } public void LoadZones() { ApplyConfigToRuntimeZones(); HeimdallVoicePlugin.Log.LogInfo((object)$"Loaded {_zones.Count} zones."); } public ZoneData GetZoneForPosition(Vector3 position) { //IL_0019: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) foreach (ZoneData zone in _zones) { float num = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(zone.x, zone.z)); if (num <= zone.radius) { return zone; } } return null; } private void CreateDefaultZonesFile() { Directory.CreateDirectory(Path.GetDirectoryName(_zoneFilePath)); List<ZoneData> list = CreateDefaultZones(); string contents = JsonConvert.SerializeObject((object)list, (Formatting)1); File.WriteAllText(_zoneFilePath, contents); } private void BindConfig(ConfigFile config) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Expected O, but got Unknown List<ZoneData> list = LoadZonesFromDisk(); if (list.Count == 0) { CreateDefaultZonesFile(); HeimdallVoicePlugin.Log.LogWarning((object)"Zones file not found. Arquivo de exemplo criado."); list = CreateDefaultZones(); } _zoneCount = config.Bind<int>("Zonas", "Quantidade de Zonas", Mathf.Clamp(list.Count, 1, 10), new ConfigDescription($"Quantidade de zonas ativas exibidas abaixo. Maximo: {10}.", (AcceptableValueBase)null, Array.Empty<object>())); _zoneCount.Value = Mathf.Clamp(_zoneCount.Value, 1, 10); _zoneCount.SettingChanged += HandleZoneSettingsChanged; for (int i = 0; i < 10; i++) { ZoneData zoneData = ((i < list.Count) ? list[i] : CreateDefaultZone(i + 1)); string text = $"Zona {i + 1}"; ZoneConfigEntrySet zoneConfigEntrySet = new ZoneConfigEntrySet { Name = config.Bind<string>(text, "Nome", NormalizeZoneName(zoneData.name, i + 1), new ConfigDescription("Nome da zona.", (AcceptableValueBase)null, Array.Empty<object>())), X = config.Bind<float>(text, "Posicao X", zoneData.x, new ConfigDescription("Posicao X da zona no mapa.", (AcceptableValueBase)null, Array.Empty<object>())), Z = config.Bind<float>(text, "Posicao Z", zoneData.z, new ConfigDescription("Posicao Z da zona no mapa.", (AcceptableValueBase)null, Array.Empty<object>())), Radius = config.Bind<float>(text, "Raio", (zoneData.radius <= 0f) ? 60f : zoneData.radius, new ConfigDescription("Raio da zona.", (AcceptableValueBase)null, Array.Empty<object>())), DiscordChannelId = config.Bind<string>(text, "ID da Sala Discord", NormalizeChannelId(zoneData.discordChannelId), new ConfigDescription("ID da sala de voz do Discord para esta zona.", (AcceptableValueBase)null, Array.Empty<object>())) }; zoneConfigEntrySet.Name.SettingChanged += HandleZoneSettingsChanged; zoneConfigEntrySet.X.SettingChanged += HandleZoneSettingsChanged; zoneConfigEntrySet.Z.SettingChanged += HandleZoneSettingsChanged; zoneConfigEntrySet.Radius.SettingChanged += HandleZoneSettingsChanged; zoneConfigEntrySet.DiscordChannelId.SettingChanged += HandleZoneSettingsChanged; _zoneEntries.Add(zoneConfigEntrySet); } PersistZonesFromConfig(); } private void HandleZoneSettingsChanged(object sender, EventArgs args) { if (!_isSynchronizing) { PersistZonesFromConfig(); } } private void PersistZonesFromConfig() { _isSynchronizing = true; try { _zoneCount.Value = Mathf.Clamp(_zoneCount.Value, 1, 10); ApplyConfigToRuntimeZones(); Directory.CreateDirectory(Path.GetDirectoryName(_zoneFilePath)); string contents = JsonConvert.SerializeObject((object)_zones, (Formatting)1); File.WriteAllText(_zoneFilePath, contents); } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)("Falha ao salvar zonas: " + ex.Message)); } } finally { _isSynchronizing = false; } } private void ApplyConfigToRuntimeZones() { List<ZoneData> list = new List<ZoneData>(); int num = Mathf.Clamp(_zoneCount?.Value ?? 1, 1, 10); for (int i = 0; i < num && i < _zoneEntries.Count; i++) { ZoneConfigEntrySet zoneConfigEntrySet = _zoneEntries[i]; list.Add(new ZoneData { name = NormalizeZoneName(zoneConfigEntrySet.Name.Value, i + 1), x = zoneConfigEntrySet.X.Value, z = zoneConfigEntrySet.Z.Value, radius = ((zoneConfigEntrySet.Radius.Value <= 0f) ? 60f : zoneConfigEntrySet.Radius.Value), discordChannelId = NormalizeChannelId(zoneConfigEntrySet.DiscordChannelId.Value) }); } _zones = list; } private List<ZoneData> LoadZonesFromDisk() { try { if (!File.Exists(_zoneFilePath)) { return new List<ZoneData>(); } string text = File.ReadAllText(_zoneFilePath); if (string.IsNullOrWhiteSpace(text)) { return new List<ZoneData>(); } return JsonConvert.DeserializeObject<List<ZoneData>>(text) ?? new List<ZoneData>(); } catch (Exception ex) { ManualLogSource log = HeimdallVoicePlugin.Log; if (log != null) { log.LogError((object)("Falha ao carregar zonas: " + ex.Message)); } return new List<ZoneData>(); } } private static List<ZoneData> CreateDefaultZones() { return new List<ZoneData> { CreateDefaultZone(1) }; } private static ZoneData CreateDefaultZone(int index) { return new ZoneData { name = ((index == 1) ? "Global" : $"Zone {index}"), x = 0f, z = 0f, radius = 60f, discordChannelId = "ID_DO_CANAL_DISCORD" }; } private static string NormalizeZoneName(string name, int index) { return string.IsNullOrWhiteSpace(name) ? $"Zone {index}" : name.Trim(); } private static string NormalizeChannelId(string channelId) { return string.IsNullOrWhiteSpace(channelId) ? "ID_DO_CANAL_DISCORD" : channelId.Trim(); } } internal class ZoneConfigEntrySet { public ConfigEntry<string> Name; public ConfigEntry<float> X; public ConfigEntry<float> Z; public ConfigEntry<float> Radius; public ConfigEntry<string> DiscordChannelId; } public class ZoneData { public string name; public float x; public float z; public float radius; public string discordChannelId; }