Decompiled source of HeimdallVoice v0.1.0

HeimdallVoice.dll

Decompiled 10 hours ago
using 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;
}