Decompiled source of ValAgentBridge v1.5.5

plugins/ValAgentBridge.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Lib.GAB;
using Lib.GAB.Events;
using Lib.GAB.Server;
using Lib.GAB.Tools;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using ValAgentBridge.Tools;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValAgentBridge")]
[assembly: AssemblyDescription("Lets an AI agent automate and monitor mod testing in a running Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("BarryWhite")]
[assembly: AssemblyProduct("ValAgentBridge")]
[assembly: AssemblyCopyright("Copyright (c) 2026 BarryWhite")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7c1e3b52-9a4d-4f0e-8b6a-2d5f1c9e4a37")]
[assembly: AssemblyFileVersion("1.5.5")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.5.5.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ValAgentBridge
{
	public static class BackpackCompat
	{
		private sealed class Source
		{
			public string mod = "";

			public MethodInfo method;

			public bool takesPlayer;
		}

		private static List<Source>? _sources;

		public static List<(string mod, Inventory inventory)> EquippedBackpacks(Player player)
		{
			List<(string, Inventory)> list = new List<(string, Inventory)>();
			if ((Object)(object)player == (Object)null)
			{
				return list;
			}
			foreach (Source item in Sources())
			{
				try
				{
					object? obj = item.method.Invoke(null, (!item.takesPlayer) ? Array.Empty<object>() : new object[1] { player });
					Inventory val = (Inventory)((obj is Inventory) ? obj : null);
					if (val != null)
					{
						list.Add((item.mod, val));
					}
				}
				catch (Exception)
				{
				}
			}
			return list;
		}

		private static List<Source> Sources()
		{
			if (_sources != null)
			{
				return _sources;
			}
			_sources = new List<Source>();
			Add("Adventure Backpacks", "AdventureBackpacks.API.ABAPI", new Type[1] { typeof(Player) });
			Add("Backpacks", "Backpacks.API", Type.EmptyTypes);
			return _sources;
		}

		private static void Add(string mod, string typeName, Type[] args)
		{
			Type type = AccessTools.TypeByName(typeName);
			if (!(type == null))
			{
				MethodInfo methodInfo = AccessTools.Method(type, "GetEquippedBackpackInventory", args, (Type[])null);
				if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ReturnType != typeof(Inventory))
				{
					ValAgentBridgePlugin.ModLogger.LogWarning((object)(mod + " API changed (GetEquippedBackpackInventory); its backpack contents won't be shown."));
					return;
				}
				_sources.Add(new Source
				{
					mod = mod,
					method = methodInfo,
					takesPlayer = (args.Length == 1)
				});
			}
		}
	}
	public static class BlockHold
	{
		private static Player? _held;

		private static bool _patched;

		private static FieldRef<Character, bool>? _blocking;

		public static string? Set(Player player, bool active)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			if (!active)
			{
				if ((Object)(object)_held != (Object)null && (Object)(object)_held == (Object)(object)player && _blocking != null)
				{
					_blocking.Invoke((Character)(object)player) = false;
				}
				_held = null;
				return null;
			}
			if (!_patched)
			{
				try
				{
					_blocking = AccessTools.FieldRefAccess<Character, bool>("m_blocking");
					MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "SetControls", (Type[])null, (Type[])null);
					if (methodInfo == null)
					{
						return "Player.SetControls not found; blocking can't be held.";
					}
					new Harmony("BarryWhite.ValAgentBridge.block").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BlockHold), "SetControlsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_patched = true;
				}
				catch (Exception ex)
				{
					return "Blocking can't be held: " + ex.GetBaseException().Message;
				}
			}
			_held = player;
			_blocking.Invoke((Character)(object)player) = true;
			return null;
		}

		private static void SetControlsPostfix(Player __instance)
		{
			if (!((Object)(object)_held == (Object)null))
			{
				if ((Object)(object)_held != (Object)(object)Player.m_localPlayer)
				{
					_held = null;
				}
				else if ((Object)(object)__instance == (Object)(object)_held)
				{
					_blocking.Invoke((Character)(object)__instance) = true;
				}
			}
		}
	}
	public static class Inspector
	{
		public sealed class PathEnd
		{
			public Type OwnerType = typeof(object);

			public object? Owner;

			public string? Member;

			public int Index = -1;

			public bool IsTypeOnly
			{
				get
				{
					if (Member == null)
					{
						return Index < 0;
					}
					return false;
				}
			}
		}

		public const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const int MaxItems = 50;

		private static readonly object IndexLock = new object();

		private static int _indexedAssemblies = -1;

		private static List<Type> _allTypes = new List<Type>();

		private static Dictionary<string, Type> _byFullName = new Dictionary<string, Type>();

		private static Dictionary<string, List<Type>> _byShortName = new Dictionary<string, List<Type>>();

		public static object? Describe(object? value, int depth, int nodeBudget = 3000)
		{
			int budget = nodeBudget;
			return Walk(value, depth, ref budget);
		}

		private static object? Walk(object? value, int depth, ref int budget)
		{
			if (--budget < 0)
			{
				return "(output limit reached)";
			}
			if (value == null)
			{
				return null;
			}
			Object val = (Object)((value is Object) ? value : null);
			if (val != null && val == (Object)null)
			{
				return "(destroyed)";
			}
			Type type = value.GetType();
			if (type.IsPrimitive || value is string || value is decimal)
			{
				return value;
			}
			if (type.IsEnum)
			{
				return value.ToString();
			}
			if (value is Type type2)
			{
				return type2.FullName;
			}
			if (value is Delegate obj)
			{
				return "(delegate " + obj.Method.DeclaringType?.Name + "." + obj.Method.Name + ")";
			}
			if (value is Vector2 || value is Vector3 || value is Vector4 || value is Quaternion || value is Color || value is Vector2Int || value is Vector3Int)
			{
				return value.ToString();
			}
			Object val2 = (Object)((value is Object) ? value : null);
			if (val2 != null && depth <= 0)
			{
				return val2.name + " (" + type.Name + ")";
			}
			if (depth <= 0)
			{
				return "(" + TypeName(type) + ")";
			}
			if (value is IDictionary dictionary)
			{
				Dictionary<string, object> dictionary2 = new Dictionary<string, object> { ["$count"] = dictionary.Count };
				int num = 0;
				foreach (DictionaryEntry item in dictionary)
				{
					if (num++ >= 50)
					{
						dictionary2["$more"] = dictionary.Count - 50;
						break;
					}
					dictionary2[item.Key?.ToString() ?? "null"] = Walk(item.Value, depth - 1, ref budget);
				}
				return dictionary2;
			}
			if (value is IEnumerable enumerable)
			{
				List<object> list = new List<object>();
				int num2 = 0;
				foreach (object item2 in enumerable)
				{
					if (num2++ >= 50)
					{
						list.Add("(more items not shown)");
						break;
					}
					list.Add(Walk(item2, depth - 1, ref budget));
				}
				return list;
			}
			Dictionary<string, object> dictionary3 = new Dictionary<string, object> { ["$type"] = TypeName(type) };
			Object val3 = (Object)((value is Object) ? value : null);
			if (val3 != null)
			{
				dictionary3["$name"] = val3.name;
			}
			List<FieldInfo> list2;
			try
			{
				list2 = InstanceFields(type).ToList();
			}
			catch (Exception ex)
			{
				dictionary3["$fields"] = "(unreadable: " + ex.GetBaseException().GetType().Name + ": " + ex.GetBaseException().Message + ")";
				return dictionary3;
			}
			foreach (FieldInfo item3 in list2)
			{
				object value2;
				try
				{
					value2 = item3.GetValue(value);
				}
				catch (Exception ex2)
				{
					value2 = "(error: " + ex2.GetType().Name + ")";
				}
				dictionary3[CleanName(item3.Name)] = Walk(value2, depth - 1, ref budget);
			}
			return dictionary3;
		}

		public static IEnumerable<FieldInfo> InstanceFields(Type type)
		{
			Type t = type;
			while (t != null && t != typeof(object) && !IsUnityType(t))
			{
				FieldInfo[] fields = t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				for (int i = 0; i < fields.Length; i++)
				{
					yield return fields[i];
				}
				t = t.BaseType;
			}
		}

		public static bool IsUnityType(Type t)
		{
			if (!(t.Namespace ?? "").StartsWith("UnityEngine"))
			{
				return t.Assembly.GetName().Name.StartsWith("UnityEngine");
			}
			return true;
		}

		public static string CleanName(string name)
		{
			if (!name.StartsWith("<") || !name.Contains(">k__BackingField"))
			{
				return name;
			}
			return name.Substring(1, name.IndexOf('>') - 1);
		}

		public static string TypeName(Type t)
		{
			if (!t.IsGenericType)
			{
				return t.Name;
			}
			return t.Name.Split(new char[1] { '`' })[0] + "<" + string.Join(",", t.GetGenericArguments().Select(TypeName)) + ">";
		}

		public static IReadOnlyList<Type> AllTypes()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			lock (IndexLock)
			{
				if (assemblies.Length == _indexedAssemblies)
				{
					return _allTypes;
				}
				List<Type> list = new List<Type>();
				Dictionary<string, Type> dictionary = new Dictionary<string, Type>();
				Dictionary<string, List<Type>> dictionary2 = new Dictionary<string, List<Type>>();
				Assembly[] array = assemblies;
				foreach (Assembly assembly in array)
				{
					if (assembly.IsDynamic)
					{
						continue;
					}
					Type[] types;
					try
					{
						types = assembly.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						types = ex.Types;
					}
					catch
					{
						continue;
					}
					Type[] array2 = types;
					foreach (Type type in array2)
					{
						if (type == null)
						{
							continue;
						}
						list.Add(type);
						string key = (type.FullName ?? type.Name).Replace('+', '.');
						if (!dictionary.ContainsKey(key))
						{
							dictionary[key] = type;
						}
						if (!type.IsNested)
						{
							if (!dictionary2.TryGetValue(type.Name, out var value))
							{
								value = (dictionary2[type.Name] = new List<Type>());
							}
							value.Add(type);
						}
					}
				}
				_allTypes = list;
				_byFullName = dictionary;
				_byShortName = dictionary2;
				_indexedAssemblies = assemblies.Length;
				return _allTypes;
			}
		}

		public static Type? ResolveType(string name, out string? ambiguous)
		{
			ambiguous = null;
			AllTypes();
			lock (IndexLock)
			{
				if (_byFullName.TryGetValue(name.Replace('+', '.'), out Type value))
				{
					return value;
				}
			}
			if (Enumerable.Contains(name, '.'))
			{
				return null;
			}
			List<Type> list;
			lock (IndexLock)
			{
				list = (_byShortName.TryGetValue(name, out List<Type> value2) ? value2 : new List<Type>());
			}
			if (list.Count == 1)
			{
				return list[0];
			}
			if (list.Count > 1)
			{
				List<Type> list2 = list.Where((Type t) => t.Assembly.GetName().Name == "assembly_valheim").ToList();
				if (list2.Count == 1)
				{
					return list2[0];
				}
				ambiguous = string.Join(", ", list.Select((Type t) => t.FullName).Take(6));
			}
			return null;
		}

		public static PathEnd? ResolvePath(string path, out string? error)
		{
			error = null;
			string[] array = path.Split(new char[1] { '.' });
			Type type = null;
			int num = 0;
			int num2 = array.Length;
			while (num2 >= 1 && type == null)
			{
				string text = string.Join(".", array.Take(num2));
				type = ResolveType(text, out string ambiguous);
				if (ambiguous != null)
				{
					error = "'" + text + "' matches several types: " + ambiguous + ". Use the full name.";
					return null;
				}
				if (type != null)
				{
					num = num2;
				}
				num2--;
			}
			if (type == null)
			{
				error = "No type found at the start of '" + path + "'. Try debug/find_types.";
				return null;
			}
			if (num == array.Length)
			{
				return new PathEnd
				{
					OwnerType = type
				};
			}
			object next = null;
			Type type2 = type;
			int num3 = num;
			while (num3 < array.Length - 1)
			{
				if (!Step(type2, next, array[num3], out next, out error))
				{
					return null;
				}
				if (next != null)
				{
					Object val = (Object)((next is Object) ? next : null);
					if (val == null || !(val == (Object)null))
					{
						type2 = next.GetType();
						num3++;
						continue;
					}
				}
				error = "'" + string.Join(".", array.Take(num3 + 1)) + "' is null";
				return null;
			}
			string text2 = array[^1];
			if (int.TryParse(text2, out var result) && next is IList)
			{
				return new PathEnd
				{
					OwnerType = type2,
					Owner = next,
					Index = result
				};
			}
			return new PathEnd
			{
				OwnerType = type2,
				Owner = next,
				Member = text2
			};
		}

		public static bool Read(PathEnd end, out object? value, out string? error)
		{
			value = null;
			error = null;
			if (end.Index >= 0)
			{
				IList list = (IList)end.Owner;
				if (end.Index >= list.Count)
				{
					error = $"Index {end.Index} is outside 0..{list.Count - 1}";
					return false;
				}
				value = list[end.Index];
				return true;
			}
			try
			{
				return TryGetMember(end.OwnerType, end.Owner, end.Member, out value, out error);
			}
			catch (Exception ex)
			{
				error = "Reading '" + end.Member + "' threw " + ex.GetBaseException().GetType().Name + ": " + ex.GetBaseException().Message;
				return false;
			}
		}

		public static bool Write(PathEnd end, string text, out object? before, out object? after, out string? error)
		{
			before = (after = null);
			error = null;
			if (end.IsTypeOnly)
			{
				error = "The path names a type, not a value.";
				return false;
			}
			if (end.Owner != null && end.OwnerType.IsValueType)
			{
				error = "'" + end.Member + "' belongs to a struct copy (" + end.OwnerType.Name + "); writing it would change nothing. Set the whole struct one level up.";
				return false;
			}
			try
			{
				if (end.Index >= 0)
				{
					IList list = (IList)end.Owner;
					Type type = (end.OwnerType.IsArray ? end.OwnerType.GetElementType() : (end.OwnerType.IsGenericType ? end.OwnerType.GetGenericArguments()[0] : typeof(object)));
					before = list[end.Index];
					list[end.Index] = ConvertText(text, type);
					after = list[end.Index];
					return true;
				}
				BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
				Type type2 = end.OwnerType;
				while (type2 != null)
				{
					FieldInfo fieldInfo = type2.GetField(end.Member, bindingFlags | BindingFlags.DeclaredOnly) ?? type2.GetField("<" + end.Member + ">k__BackingField", bindingFlags | BindingFlags.DeclaredOnly);
					if (fieldInfo != null)
					{
						if (fieldInfo.IsLiteral)
						{
							error = "'" + end.Member + "' is a constant";
							return false;
						}
						if (!fieldInfo.IsStatic && end.Owner == null)
						{
							error = "'" + end.Member + "' needs an instance";
							return false;
						}
						object obj = (fieldInfo.IsStatic ? null : end.Owner);
						before = fieldInfo.GetValue(obj);
						fieldInfo.SetValue(obj, ConvertText(text, fieldInfo.FieldType));
						after = fieldInfo.GetValue(obj);
						return true;
					}
					PropertyInfo property = type2.GetProperty(end.Member, bindingFlags | BindingFlags.DeclaredOnly);
					if (property != null && property.GetIndexParameters().Length == 0)
					{
						MethodInfo setMethod = property.GetSetMethod(nonPublic: true);
						if (setMethod == null)
						{
							error = "'" + end.Member + "' is read-only";
							return false;
						}
						if (!setMethod.IsStatic && end.Owner == null)
						{
							error = "'" + end.Member + "' needs an instance";
							return false;
						}
						object obj2 = (setMethod.IsStatic ? null : end.Owner);
						before = property.GetGetMethod(nonPublic: true)?.Invoke(obj2, null);
						setMethod.Invoke(obj2, new object[1] { ConvertText(text, property.PropertyType) });
						after = property.GetGetMethod(nonPublic: true)?.Invoke(obj2, null);
						return true;
					}
					type2 = type2.BaseType;
				}
				error = end.OwnerType.Name + " has no field or property '" + end.Member + "'";
				return false;
			}
			catch (Exception ex)
			{
				error = "Could not set '" + end.Member + "': " + ex.GetBaseException().Message;
				return false;
			}
		}

		public static object? ConvertText(string text, Type type)
		{
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			if (type == typeof(string))
			{
				return text;
			}
			if (text == "null" && !type.IsValueType)
			{
				return null;
			}
			Type type2 = Nullable.GetUnderlyingType(type) ?? type;
			if (type2.IsEnum)
			{
				return Enum.Parse(type2, text, ignoreCase: true);
			}
			if (type2.IsPrimitive || type2 == typeof(decimal))
			{
				return Convert.ChangeType(text, type2, CultureInfo.InvariantCulture);
			}
			if (type2 == typeof(Vector3) || type2 == typeof(Vector2))
			{
				float[] array = (from s in text.Trim('(', ')', '[', ']', ' ').Split(new char[1] { ',' })
					select float.Parse(s.Trim(), CultureInfo.InvariantCulture)).ToArray();
				if (!(type2 == typeof(Vector2)))
				{
					return (object)new Vector3(array[0], (array.Length > 1) ? array[1] : 0f, (array.Length > 2) ? array[2] : 0f);
				}
				return (object)new Vector2(array[0], array[1]);
			}
			return JsonConvert.DeserializeObject(text, type2);
		}

		private static bool Step(Type type, object? instance, string seg, out object? next, out string? error)
		{
			next = null;
			error = null;
			if (int.TryParse(seg, out var result) && instance is IList list)
			{
				if (result < 0 || result >= list.Count)
				{
					error = $"Index {result} is outside 0..{list.Count - 1}";
					return false;
				}
				next = list[result];
				return true;
			}
			try
			{
				return TryGetMember(type, instance, seg, out next, out error);
			}
			catch (Exception ex)
			{
				error = "Reading '" + seg + "' threw " + ex.GetBaseException().GetType().Name + ": " + ex.GetBaseException().Message;
				return false;
			}
		}

		public static bool TryGetMember(Type type, object? instance, string name, out object? value, out string? error)
		{
			value = null;
			error = null;
			BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo fieldInfo = type2.GetField(name, bindingFlags | BindingFlags.DeclaredOnly) ?? type2.GetField("<" + name + ">k__BackingField", bindingFlags | BindingFlags.DeclaredOnly);
				if (fieldInfo != null)
				{
					if (!fieldInfo.IsStatic && instance == null)
					{
						error = "'" + name + "' needs an instance";
						return false;
					}
					value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : instance);
					return true;
				}
				PropertyInfo property = type2.GetProperty(name, bindingFlags | BindingFlags.DeclaredOnly);
				if (property != null && property.GetIndexParameters().Length == 0 && property.GetGetMethod(nonPublic: true) != null)
				{
					MethodInfo getMethod = property.GetGetMethod(nonPublic: true);
					if (!getMethod.IsStatic && instance == null)
					{
						error = "'" + name + "' needs an instance";
						return false;
					}
					value = getMethod.Invoke(getMethod.IsStatic ? null : instance, null);
					return true;
				}
				type2 = type2.BaseType;
			}
			error = type.Name + " has no field or property '" + name + "'";
			return false;
		}
	}
	public class MainThreadDispatcher : MonoBehaviour
	{
		private static MainThreadDispatcher? _instance;

		private static readonly ConcurrentQueue<Action> ExecutionQueue = new ConcurrentQueue<Action>();

		private static int _mainThreadId = -1;

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static long _lastFrameMs = -1L;

		private static float _smoothedFrameMs;

		public static MainThreadDispatcher Instance
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("ValAgentBridge_MainThreadDispatcher");
					_instance = val.AddComponent<MainThreadDispatcher>();
					Object.DontDestroyOnLoad((Object)val);
				}
				return _instance;
			}
		}

		public static long FrameAgeMs
		{
			get
			{
				if (Interlocked.Read(in _lastFrameMs) >= 0)
				{
					return Clock.ElapsedMilliseconds - Interlocked.Read(in _lastFrameMs);
				}
				return -1L;
			}
		}

		public static float FrameMs => _smoothedFrameMs;

		public static void Init()
		{
			_mainThreadId = Thread.CurrentThread.ManagedThreadId;
			_ = Instance;
		}

		public void Enqueue(Action action)
		{
			ExecutionQueue.Enqueue(action);
		}

		public static T Run<T>(Func<T> work, int timeoutMs = 10000)
		{
			if (Thread.CurrentThread.ManagedThreadId == _mainThreadId)
			{
				return work();
			}
			TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
			Instance.Enqueue(delegate
			{
				try
				{
					tcs.SetResult(work());
				}
				catch (Exception exception)
				{
					tcs.SetException(exception);
				}
			});
			if (!tcs.Task.Wait(timeoutMs))
			{
				throw new TimeoutException("The game's main thread did not answer in time (loading screen or paused?)");
			}
			return tcs.Task.Result;
		}

		public static object RunTool(Func<object> work, int timeoutMs = 15000)
		{
			try
			{
				return Run(work, timeoutMs);
			}
			catch (TimeoutException)
			{
				return new
				{
					success = false,
					error = "The game's main thread did not answer in time (loading screen or paused?). The action may still happen when the game catches up."
				};
			}
			catch (Exception ex2)
			{
				Exception baseException = ex2.GetBaseException();
				return new
				{
					success = false,
					error = baseException.GetType().Name + ": " + baseException.Message
				};
			}
		}

		private void Update()
		{
			Interlocked.Exchange(ref _lastFrameMs, Clock.ElapsedMilliseconds);
			float num = Time.unscaledDeltaTime * 1000f;
			_smoothedFrameMs = ((_smoothedFrameMs <= 0f) ? num : (_smoothedFrameMs * 0.9f + num * 0.1f));
			Action result;
			while (ExecutionQueue.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception arg)
				{
					ValAgentBridgePlugin.ModLogger.LogError((object)$"Queued bridge work failed: {arg}");
				}
			}
		}
	}
	public static class ModCommands
	{
		public sealed class Found
		{
			public string name = "";

			public string mod = "";

			public string method = "";

			public string? description;

			public bool registered;

			public string? problem;
		}

		public static readonly List<Found> All = new List<Found>();

		public static void RegisterAll(IToolRegistry registry)
		{
			foreach (var item3 in from p in Chainloader.PluginInfos.Values
				where (Object)(object)p.Instance != (Object)null
				select (mod: p.Metadata.Name, asm: ((object)p.Instance).GetType().Assembly) into x
				where x.asm != typeof(ModCommands).Assembly
				group x by x.asm into g
				select g.First())
			{
				string item = item3.mod;
				Assembly item2 = item3.asm;
				int num = 0;
				Type[] types;
				try
				{
					types = item2.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					types = ex.Types;
				}
				catch
				{
					continue;
				}
				foreach (Type item4 in types.Where((Type t) => t != null))
				{
					MethodInfo[] methods;
					try
					{
						methods = item4.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					catch
					{
						num++;
						continue;
					}
					MethodInfo[] array = methods;
					foreach (MethodInfo methodInfo in array)
					{
						try
						{
							object obj3 = methodInfo.GetCustomAttributes(inherit: false).FirstOrDefault((object a) => a.GetType().Name == "BridgeToolAttribute");
							if (obj3 != null)
							{
								Register(registry, item, methodInfo, obj3);
							}
						}
						catch (Exception)
						{
							num++;
						}
					}
				}
				if (num > 0)
				{
					ValAgentBridgePlugin.ModLogger.LogInfo((object)$"Mod commands: skipped {num} part(s) of {item} that need a mod which isn't installed.");
				}
			}
			if (All.Count > 0)
			{
				ValAgentBridgePlugin.ModLogger.LogInfo((object)$"Mod commands: {All.Count((Found f) => f.registered)} registered, {All.Count((Found f) => !f.registered)} skipped");
			}
		}

		private static void Register(IToolRegistry registry, string mod, MethodInfo method, object attr)
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			Found found = new Found
			{
				mod = mod,
				method = method.DeclaringType?.FullName + "." + method.Name,
				name = (Read(attr, "Name") ?? ""),
				description = Read(attr, "Description")
			};
			All.Add(found);
			if (!ToolNameValidator.IsValid(found.name))
			{
				found.problem = "name '" + found.name + "' must look like 'mymod/do_thing' (lowercase, at least one '/')";
				ValAgentBridgePlugin.ModLogger.LogWarning((object)("Mod command from " + mod + " skipped: " + found.problem));
				return;
			}
			if (registry.HasTool(found.name))
			{
				found.problem = "a command with this name already exists";
				ValAgentBridgePlugin.ModLogger.LogWarning((object)("Mod command " + found.name + " from " + mod + " skipped: " + found.problem));
				return;
			}
			ParameterInfo[] parameters = method.GetParameters();
			ToolInfo info = new ToolInfo
			{
				Name = found.name,
				Description = "[" + mod + "] " + found.description,
				Parameters = ((IEnumerable<ParameterInfo>)parameters).Select((Func<ParameterInfo, ToolParameterInfo>)((ParameterInfo p) => new ToolParameterInfo
				{
					Name = (p.Name ?? ""),
					Type = p.ParameterType,
					Required = !p.HasDefaultValue,
					DefaultValue = (p.HasDefaultValue ? p.DefaultValue : null)
				})).ToList()
			};
			registry.RegisterTool(found.name, (Func<object, Task<object>>)delegate(object input)
			{
				string text = ToolGuard.CheckArgs(info, input);
				return Task.FromResult((text != null) ? new
				{
					success = false,
					error = text
				} : Invoke(method, parameters, input));
			}, info);
			found.registered = true;
		}

		private static object Invoke(MethodInfo method, ParameterInfo[] parameters, object input)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			JObject args;
			try
			{
				args = (JObject)((input != null) ? ((object)((input is string text) ? JObject.Parse(text) : JObject.FromObject(input))) : ((object)new JObject()));
			}
			catch (Exception ex)
			{
				return new
				{
					success = false,
					error = "Bad arguments: " + ex.Message
				};
			}
			object?[] values;
			try
			{
				values = parameters.Select(delegate(ParameterInfo p)
				{
					//IL_003a: Unknown result type (might be due to invalid IL or missing references)
					//IL_0041: Invalid comparison between Unknown and I4
					JProperty? obj = args.Properties().FirstOrDefault((Func<JProperty, bool>)((JProperty x) => string.Equals(x.Name, p.Name, StringComparison.OrdinalIgnoreCase)));
					JToken val = ((obj != null) ? obj.Value : null);
					return (val == null || (int)val.Type == 10) ? ((!p.HasDefaultValue) ? ((!p.ParameterType.IsValueType) ? null : Activator.CreateInstance(p.ParameterType)) : p.DefaultValue) : val.ToObject(p.ParameterType);
				}).ToArray();
			}
			catch (Exception ex2)
			{
				return new
				{
					success = false,
					error = "Bad arguments: " + ex2.Message
				};
			}
			return MainThreadDispatcher.RunTool(() => method.Invoke(null, values) ?? new
			{
				success = true
			}, 30000);
		}

		private static string? Read(object attr, string property)
		{
			PropertyInfo property2 = attr.GetType().GetProperty(property);
			if (property2 != null)
			{
				return property2.GetValue(attr) as string;
			}
			return attr.GetType().GetField(property)?.GetValue(attr) as string;
		}
	}
	public class MovementManager : MonoBehaviour
	{
		private static MovementManager? _instance;

		private bool _isMoving;

		private TaskCompletionSource<object>? _tcs;

		private float _startTime;

		private float _duration;

		private Vector3 _moveDir;

		private bool _run;

		public static MovementManager Instance
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("ValAgentBridge_MovementManager");
					_instance = val.AddComponent<MovementManager>();
					Object.DontDestroyOnLoad((Object)val);
				}
				return _instance;
			}
		}

		public Task<object> StartMoving(Vector3 direction, bool run, float duration)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			StopMoving();
			_moveDir = ((Vector3)(ref direction)).normalized;
			_run = run;
			_duration = duration;
			_startTime = Time.time;
			_isMoving = true;
			_tcs = new TaskCompletionSource<object>();
			return _tcs.Task;
		}

		public void StopMoving()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (_isMoving)
			{
				_isMoving = false;
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).SetMoveDir(Vector3.zero);
					((Character)localPlayer).SetRun(false);
				}
			}
		}

		private void FixedUpdate()
		{
			if (!_isMoving || _tcs == null)
			{
				return;
			}
			try
			{
				Step();
			}
			catch (Exception ex)
			{
				StopMoving();
				_tcs?.TrySetResult(new
				{
					success = false,
					message = "Moving failed: " + ex.GetBaseException().Message
				});
			}
		}

		private void Step()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Finish("Player lost");
				return;
			}
			if (Time.time - _startTime >= _duration)
			{
				Finish("Completed");
				return;
			}
			((Character)localPlayer).SetMoveDir(_moveDir);
			((Character)localPlayer).SetLookDir(_moveDir, 0f);
			((Character)localPlayer).SetRun(_run);
		}

		private void Finish(string message)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Vector3 val = (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero);
			StopMoving();
			_tcs?.TrySetResult(new
			{
				success = true,
				message = message,
				position = new { val.x, val.y, val.z }
			});
		}
	}
	public class NavigationManager : MonoBehaviour
	{
		private static NavigationManager? _instance;

		private Vector3 _targetPos;

		private readonly List<Vector3> _navPath = new List<Vector3>();

		private bool _isNavigating;

		private TaskCompletionSource<object>? _tcs;

		private float _startTime;

		private float _timeout;

		private float _lastPathCalcTime;

		private const float ArrivalDistXZ = 1.5f;

		private const float WaypointAdvanceDist = 1.5f;

		private const float PathRecalcInterval = 1f;

		private const float RunDistanceThreshold = 5f;

		public static NavigationManager Instance
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("ValAgentBridge_NavigationManager");
					_instance = val.AddComponent<NavigationManager>();
					Object.DontDestroyOnLoad((Object)val);
				}
				return _instance;
			}
		}

		public Task<object> StartNavigation(Vector3 target, float timeout)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			StopNavigation("Cancelled by new navigation request");
			_targetPos = target;
			_timeout = timeout;
			_startTime = Time.time;
			_lastPathCalcTime = 0f;
			_navPath.Clear();
			_isNavigating = true;
			_tcs = new TaskCompletionSource<object>();
			ValAgentBridgePlugin.ModLogger.LogInfo((object)$"Navigation started to ({target.x:F1}, {target.z:F1}), timeout={timeout}s");
			return _tcs.Task;
		}

		public void StopNavigation(string? reason = null)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (_isNavigating)
			{
				_isNavigating = false;
				_navPath.Clear();
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).SetMoveDir(Vector3.zero);
					((Character)localPlayer).SetRun(false);
				}
				if (reason != null)
				{
					ValAgentBridgePlugin.ModLogger.LogInfo((object)("Navigation stopped: " + reason));
				}
			}
		}

		private void FixedUpdate()
		{
			if (!_isNavigating || _tcs == null)
			{
				return;
			}
			try
			{
				Step();
			}
			catch (Exception ex)
			{
				Finish(success: false, "Navigation failed: " + ex.GetBaseException().Message);
			}
		}

		private void Step()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Finish(success: false, "Player lost");
				return;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			if (Time.time - _startTime > _timeout)
			{
				float num = Utils.DistanceXZ(position, _targetPos);
				Finish(success: false, $"Timeout after {_timeout}s (distance remaining: {num:F1}m)");
				return;
			}
			float num2 = Utils.DistanceXZ(position, _targetPos);
			if (num2 < 1.5f)
			{
				Finish(success: true, $"Arrived (distance: {num2:F2}m)");
			}
			else
			{
				if (!((Character)localPlayer).CanMove())
				{
					return;
				}
				if (Time.time - _lastPathCalcTime > 1f)
				{
					_lastPathCalcTime = Time.time;
					if ((Object)(object)Pathfinding.instance == (Object)null)
					{
						Finish(success: false, "Pathfinding isn't available (still loading?)");
						return;
					}
					if (!Pathfinding.instance.GetPath(position, _targetPos, _navPath, (AgentType)1, false, true, false) || _navPath.Count == 0)
					{
						Finish(success: false, "No path found");
						return;
					}
				}
				if (_navPath.Count != 0)
				{
					while (_navPath.Count > 1 && Utils.DistanceXZ(position, _navPath[0]) < 1.5f)
					{
						_navPath.RemoveAt(0);
					}
					Vector3 val = _navPath[0] - position;
					val.y = 0f;
					if (((Vector3)(ref val)).magnitude > 0.1f)
					{
						((Vector3)(ref val)).Normalize();
					}
					((Character)localPlayer).SetMoveDir(val);
					((Character)localPlayer).SetLookDir(val, 0f);
					((Character)localPlayer).SetRun(num2 > 5f);
				}
			}
		}

		private void Finish(bool success, string message)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			Vector3 val = (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero);
			float num = (((Object)(object)localPlayer != (Object)null) ? Utils.DistanceXZ(val, _targetPos) : (-1f));
			StopNavigation(message);
			_tcs?.TrySetResult(new
			{
				success = success,
				message = message,
				finalPosition = new { val.x, val.y, val.z },
				distanceToTarget = (float)Math.Round(num, 2)
			});
		}
	}
	public static class PlayerEvents
	{
		private static IEventManager? _events;

		private static float _lastHealth = -1f;

		private static float _nextSample;

		public static void Install(Harmony harmony, IEventManager events)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			_events = events;
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "OnDeath", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PlayerEvents), "OnDeathPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				ValAgentBridgePlugin.ModLogger.LogWarning((object)"Player.OnDeath not found; player/death events won't be sent.");
			}
		}

		public static void Tick()
		{
			if (Time.unscaledTime < _nextSample)
			{
				return;
			}
			_nextSample = Time.unscaledTime + 0.5f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_lastHealth = -1f;
				return;
			}
			float health = ((Character)localPlayer).GetHealth();
			if (_lastHealth < 0f)
			{
				_lastHealth = health;
				return;
			}
			float num = health - _lastHealth;
			if (!(Mathf.Abs(num) < 1f))
			{
				_lastHealth = health;
				Send("player/health_changed", new
				{
					health = (float)Math.Round(health, 1),
					maxHealth = (float)Math.Round(((Character)localPlayer).GetMaxHealth(), 1),
					change = (float)Math.Round(num, 1)
				});
			}
		}

		private static void OnDeathPostfix(Player __instance)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Vector3 position = ((Component)__instance).transform.position;
				Send("player/death", new
				{
					position = new
					{
						x = (float)Math.Round(position.x, 1),
						y = (float)Math.Round(position.y, 1),
						z = (float)Math.Round(position.z, 1)
					},
					biome = ((object)__instance.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString()
				});
				_lastHealth = -1f;
			}
		}

		private static void Send(string channel, object payload)
		{
			Recorder.AddEvent(channel, payload);
			try
			{
				IEventManager? events = _events;
				if (events != null)
				{
					events.EmitEventAsync(channel, payload, (DateTimeOffset?)null).ContinueWith(delegate(Task t)
					{
						_ = t.Exception;
					});
				}
			}
			catch (Exception)
			{
			}
		}
	}
	public static class PlayerLook
	{
		private static readonly FieldRef<Character, Quaternion> LookYaw = AccessTools.FieldRefAccess<Character, Quaternion>("m_lookYaw");

		private static readonly FieldRef<Player, float> LookPitch = AccessTools.FieldRefAccess<Player, float>("m_lookPitch");

		public static void Set(Player player, float heading, float tilt)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			tilt = Mathf.Clamp(tilt, -80f, 80f);
			LookYaw.Invoke((Character)(object)player) = Quaternion.Euler(0f, heading, 0f);
			LookPitch.Invoke(player) = 0f - tilt;
			((Character)player).SetLookDir(Quaternion.Euler(0f - tilt, heading, 0f) * Vector3.forward, 0f);
			player.FaceLookDirection();
		}

		public static bool Face(Player player, Vector3 direction)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			direction.y = 0f;
			if (((Vector3)(ref direction)).magnitude < 0.01f)
			{
				return false;
			}
			Set(player, Mathf.Atan2(direction.x, direction.z) * 57.29578f, 0f);
			return true;
		}
	}
	public class BridgeConfig
	{
		[JsonProperty("port")]
		public int Port { get; set; }

		[JsonProperty("token")]
		public string Token { get; set; } = "";

		[JsonProperty("gameId")]
		public string GameId { get; set; } = "valheim";
	}
	[BepInPlugin("BarryWhite.ValAgentBridge", "ValAgentBridge", "1.5.5")]
	public class ValAgentBridgePlugin : BaseUnityPlugin
	{
		private const string ModName = "ValAgentBridge";

		private const string ModVersion = "1.5.5";

		private const string Author = "BarryWhite";

		private const string ModGUID = "BarryWhite.ValAgentBridge";

		public static readonly ManualLogSource ModLogger = Logger.CreateLogSource("ValAgentBridge");

		private GabpServer? _server;

		private Harmony? _harmony;

		private bool _bridgeUp;

		public static IToolRegistry? Tools { get; private set; }

		public void Start()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			ModLogger.LogInfo((object)"ValAgentBridge v1.5.5 is loading...");
			if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GABP_SERVER_PORT")) && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GABS_BRIDGE_PATH")))
			{
				ModLogger.LogInfo((object)"Not started by GABS, so the bridge stays off.");
				return;
			}
			try
			{
				_harmony = new Harmony("BarryWhite.ValAgentBridge");
				Harmony recorderHarmony = _harmony;
				Optional("Log and message recording", delegate
				{
					Recorder.Install(recorderHarmony);
				});
				BridgeConfig bridgeConfig = ReadBridgeConfig();
				ModLogger.LogInfo((object)$"GABP config loaded - GameId: {bridgeConfig.GameId}, Port: {bridgeConfig.Port}");
				_server = Gabp.CreateServerWithExternalConfig("Valheim", "1.5.5", bridgeConfig.Port, bridgeConfig.Token, bridgeConfig.GameId);
				ToolGuard.RegisterChecked(_server.Tools, new PlayerTools());
				ToolGuard.RegisterChecked(_server.Tools, new ZNetSceneTools());
				ToolGuard.RegisterChecked(_server.Tools, new TerminalTools());
				ToolGuard.RegisterChecked(_server.Tools, new WorldTools());
				ToolGuard.RegisterChecked(_server.Tools, new ModTools());
				ToolGuard.RegisterChecked(_server.Tools, new ScreenTools());
				ToolGuard.RegisterChecked(_server.Tools, new LogTools());
				ToolGuard.RegisterChecked(_server.Tools, new UiTools());
				ToolGuard.RegisterChecked(_server.Tools, new TimeTools());
				ToolGuard.RegisterChecked(_server.Tools, new EditTools());
				ToolGuard.RegisterChecked(_server.Tools, new WatchTools());
				ToolGuard.RegisterChecked(_server.Tools, new TestTools());
				ToolGuard.RegisterChecked(_server.Tools, new ItemTools());
				ToolGuard.RegisterChecked(_server.Tools, new ControlTools());
				ToolGuard.RegisterChecked(_server.Tools, new InputTools());
				Optional("Commands from other mods", delegate
				{
					ModCommands.RegisterAll(_server.Tools);
				});
				Tools = _server.Tools;
				_server.Events.RegisterChannel("player/death", "Player death events");
				_server.Events.RegisterChannel("player/health_changed", "Player health change events");
				Harmony harmony = _harmony;
				IEventManager events = _server.Events;
				Optional("Player events", delegate
				{
					PlayerEvents.Install(harmony, events);
				});
				_bridgeUp = true;
				MainThreadDispatcher.Init();
				_ = NavigationManager.Instance;
				_ = MovementManager.Instance;
				_server.StartAsync(default(CancellationToken)).ContinueWith(delegate(Task task)
				{
					if (task.IsFaulted)
					{
						ModLogger.LogError((object)("GABP server failed to start: " + task.Exception?.InnerException?.Message));
					}
					else
					{
						ModLogger.LogInfo((object)$"GABP server listening on 127.0.0.1:{_server.Port}");
						ModLogger.LogInfo((object)"ValAgentBridge loaded successfully!");
					}
				});
			}
			catch (Exception ex)
			{
				ModLogger.LogError((object)("Failed to initialize GABP server: " + ex.Message));
				ModLogger.LogError((object)("Stack trace: " + ex.StackTrace));
			}
		}

		private static void Optional(string what, Action step)
		{
			try
			{
				step();
			}
			catch (Exception ex)
			{
				Exception baseException = ex.GetBaseException();
				ModLogger.LogWarning((object)(what + " unavailable (" + baseException.GetType().Name + ": " + baseException.Message + "); the bridge runs without it."));
			}
		}

		private BridgeConfig ReadBridgeConfig()
		{
			string environmentVariable = Environment.GetEnvironmentVariable("GABS_GAME_ID");
			string environmentVariable2 = Environment.GetEnvironmentVariable("GABP_SERVER_PORT");
			string environmentVariable3 = Environment.GetEnvironmentVariable("GABP_TOKEN");
			if (!string.IsNullOrEmpty(environmentVariable2) && !string.IsNullOrEmpty(environmentVariable3) && int.TryParse(environmentVariable2, out var result))
			{
				ModLogger.LogInfo((object)"Config loaded from environment variables");
				return new BridgeConfig
				{
					Port = result,
					Token = environmentVariable3,
					GameId = (environmentVariable ?? "valheim")
				};
			}
			string environmentVariable4 = Environment.GetEnvironmentVariable("GABS_BRIDGE_PATH");
			if (!string.IsNullOrEmpty(environmentVariable4) && File.Exists(environmentVariable4))
			{
				ModLogger.LogInfo((object)("Config loaded from bridge file: " + environmentVariable4));
				return JsonConvert.DeserializeObject<BridgeConfig>(File.ReadAllText(environmentVariable4)) ?? throw new Exception("Failed to parse bridge.json");
			}
			throw new Exception("GABP server config not found. Start the game through GABS. Expected: GABP_SERVER_PORT and GABP_TOKEN environment variables, or GABS_BRIDGE_PATH.");
		}

		private void Update()
		{
			if (_bridgeUp)
			{
				PlayerEvents.Tick();
			}
		}

		private void OnDestroy()
		{
			ModLogger.LogInfo((object)"ValAgentBridge is unloading...");
			if (_server == null)
			{
				return;
			}
			try
			{
				_server.StopAsync(default(CancellationToken)).ContinueWith(delegate(Task task)
				{
					if (task.IsFaulted)
					{
						ModLogger.LogError((object)("Error stopping GABP server: " + task.Exception?.InnerException?.Message));
					}
					else
					{
						ModLogger.LogInfo((object)"GABP server stopped");
					}
				});
				_server.Dispose();
			}
			catch (Exception ex)
			{
				ModLogger.LogError((object)("Error disposing GABP server: " + ex.Message));
			}
		}
	}
	public static class Recorder
	{
		public sealed class LogEntry
		{
			public long seq;

			public string time = "";

			public string level = "";

			public string source = "";

			public string text = "";
		}

		public sealed class MessageEntry
		{
			public long seq;

			public string time = "";

			public string type = "";

			public string text = "";
		}

		public sealed class EventEntry
		{
			public long seq;

			public string time = "";

			public string channel = "";

			public object? data;
		}

		private sealed class Listener : ILogListener, IDisposable
		{
			public void LogEvent(object sender, LogEventArgs e)
			{
				//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)
				try
				{
					string text = ((object)e.Level/*cast due to .constrained prefix*/).ToString();
					string text2;
					try
					{
						text2 = e.Data?.ToString() ?? "";
					}
					catch (Exception ex)
					{
						text2 = "(log line could not be read: " + ex.GetType().Name + ")";
					}
					LogEntry logEntry = new LogEntry();
					logEntry.time = DateTime.Now.ToString("HH:mm:ss.fff");
					logEntry.level = (text.Contains(",") ? text.Split(new char[1] { ',' })[0].Trim() : text);
					ILogSource source = e.Source;
					logEntry.source = ((source != null) ? source.SourceName : null) ?? "";
					logEntry.text = text2;
					LogEntry logEntry2 = logEntry;
					lock (Gate)
					{
						logEntry2.seq = ++_logSeq;
						Log.AddLast(logEntry2);
						while (Log.Count > 3000)
						{
							Log.RemoveFirst();
						}
					}
				}
				catch
				{
				}
			}

			public void Dispose()
			{
			}
		}

		private static readonly LinkedList<EventEntry> Events = new LinkedList<EventEntry>();

		private static long _eventSeq;

		private const int MaxLog = 3000;

		private const int MaxMessages = 200;

		private static readonly object Gate = new object();

		private static readonly LinkedList<LogEntry> Log = new LinkedList<LogEntry>();

		private static readonly LinkedList<MessageEntry> Messages = new LinkedList<MessageEntry>();

		private static long _logSeq;

		private static long _msgSeq;

		private static List<string>? _consoleCapture;

		public static long LastEventSeq
		{
			get
			{
				lock (Gate)
				{
					return _eventSeq;
				}
			}
		}

		public static long LastLogSeq
		{
			get
			{
				lock (Gate)
				{
					return _logSeq;
				}
			}
		}

		public static long LastMessageSeq
		{
			get
			{
				lock (Gate)
				{
					return _msgSeq;
				}
			}
		}

		public static void AddEvent(string channel, object data)
		{
			lock (Gate)
			{
				Events.AddLast(new EventEntry
				{
					seq = ++_eventSeq,
					time = DateTime.Now.ToString("HH:mm:ss.fff"),
					channel = channel,
					data = data
				});
				while (Events.Count > 500)
				{
					Events.RemoveFirst();
				}
			}
		}

		public static List<EventEntry> EventsSince(long since, string channel, int limit)
		{
			lock (Gate)
			{
				return Events.Where((EventEntry e) => e.seq > since && (channel.Length == 0 || e.channel == channel)).Reverse().Take(limit)
					.Reverse()
					.ToList();
			}
		}

		public static void Install(Harmony harmony)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			Logger.Listeners.Add((ILogListener)(object)new Listener());
			MethodInfo methodInfo = AccessTools.Method(typeof(MessageHud), "ShowMessage", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Recorder), "ShowMessagePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				ValAgentBridgePlugin.ModLogger.LogWarning((object)"MessageHud.ShowMessage not found; ui/messages will stay empty.");
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Terminal), "AddString", new Type[1] { typeof(string) }, (Type[])null);
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Recorder), "AddStringPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		public static List<string> CaptureConsole(Action run)
		{
			List<string> result = (_consoleCapture = new List<string>());
			try
			{
				run();
				return result;
			}
			finally
			{
				_consoleCapture = null;
			}
		}

		private static void AddStringPostfix(string text)
		{
			_consoleCapture?.Add(text);
		}

		public static List<LogEntry> LogSince(long since, string minLevel, string filter, int limit)
		{
			HashSet<string> allowed = AllowedLevels(minLevel);
			lock (Gate)
			{
				return Log.Where((LogEntry e) => e.seq > since && allowed.Contains(e.level) && (filter.Length == 0 || e.text.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || e.source.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)).Reverse().Take(limit)
					.Reverse()
					.ToList();
			}
		}

		public static List<MessageEntry> MessagesSince(long since, int limit)
		{
			lock (Gate)
			{
				return Messages.Where((MessageEntry m) => m.seq > since).Reverse().Take(limit)
					.Reverse()
					.ToList();
			}
		}

		private static HashSet<string> AllowedLevels(string minLevel)
		{
			string[] source = new string[6] { "Fatal", "Error", "Warning", "Message", "Info", "Debug" };
			return new HashSet<string>(source.Take((minLevel ?? "").ToLowerInvariant() switch
			{
				"error" => 1, 
				"warning" => 2, 
				"all" => 5, 
				"debug" => 5, 
				_ => 4, 
			} + 1));
		}

		private unsafe static void ShowMessagePostfix(MessageType type, string text)
		{
			try
			{
				string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
				lock (Gate)
				{
					Messages.AddLast(new MessageEntry
					{
						seq = ++_msgSeq,
						time = DateTime.Now.ToString("HH:mm:ss.fff"),
						type = ((object)(*(MessageType*)(&type))/*cast due to .constrained prefix*/).ToString(),
						text = text2
					});
					while (Messages.Count > 200)
					{
						Messages.RemoveFirst();
					}
				}
			}
			catch
			{
			}
		}
	}
	public static class ToolGuard
	{
		public static void RegisterChecked(IToolRegistry target, object instance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			ToolRegistry inner = new ToolRegistry();
			inner.RegisterToolsFromInstance(instance);
			foreach (ToolInfo info in inner.GetTools())
			{
				string name = info.Name;
				target.RegisterTool(name, (Func<object, Task<object>>)delegate(object input)
				{
					string text = CheckArgs(info, input);
					return (text == null) ? inner.CallToolAsync(name, input) : Task.FromResult((object)new
					{
						success = false,
						error = text
					});
				}, info);
			}
		}

		public static string? CheckArgs(ToolInfo info, object? input)
		{
			JObject val;
			try
			{
				val = (JObject)((input == null) ? null : (((input is JObject) ? input : null) ?? ((input is string text) ? JObject.Parse(text) : JObject.FromObject(input))));
			}
			catch
			{
				return null;
			}
			List<ToolParameterInfo> known = info.Parameters;
			List<string> given = ((val != null) ? (from p in val.Properties()
				select p.Name).ToList() : null) ?? new List<string>();
			List<string> list = given.Where((string g) => !known.Any((ToolParameterInfo p) => string.Equals(p.Name, g, StringComparison.OrdinalIgnoreCase))).ToList();
			List<string> list2 = (from p in known
				where p.Required && !given.Any((string g) => string.Equals(p.Name, g, StringComparison.OrdinalIgnoreCase))
				select p.Name).ToList();
			if (list.Count == 0 && list2.Count == 0)
			{
				return null;
			}
			List<string> list3 = new List<string>();
			if (list.Count > 0)
			{
				list3.Add("unknown argument" + ((list.Count > 1) ? "s" : "") + " " + string.Join(", ", list.Select((string u) => "'" + u + "'")));
			}
			if (list2.Count > 0)
			{
				list3.Add("missing " + string.Join(", ", list2.Select((string m) => "'" + m + "'")));
			}
			return info.Name + ": " + string.Join("; ", list3) + ". It takes: " + Describe(known) + ".";
		}

		private static string Describe(List<ToolParameterInfo> parameters)
		{
			if (parameters.Count == 0)
			{
				return "no arguments";
			}
			return string.Join(", ", parameters.Select((ToolParameterInfo p) => (!p.Required) ? p.Name : (p.Name + " (required)")));
		}

		public static List<string> Suggest(IToolRegistry registry, string wanted, int max = 3)
		{
			wanted = wanted.Replace('.', '/').ToLowerInvariant();
			string area = (Enumerable.Contains(wanted, '/') ? wanted.Substring(0, wanted.IndexOf('/')) : "");
			string verb = wanted.Substring(wanted.IndexOf('/') + 1);
			return (from x in (from x in (from t in registry.GetTools()
						select t.Name).Select(delegate(string n)
					{
						string text = n.ToLowerInvariant();
						string text2 = text.Substring(0, Math.Max(0, text.IndexOf('/')));
						string nVerb = text.Substring(text.IndexOf('/') + 1);
						int num = Distance(verb, nVerb) + ((!(text2 == area)) ? 6 : 0);
						if (verb.Split(new char[1] { '_' }).Any((string w) => w.Length > 2 && nVerb.Contains(w)))
						{
							num -= 3;
						}
						return (n: n, score: num);
					})
					orderby x.score, x.n
					select x).Take(max)
				select x.n).ToList();
		}

		private static int Distance(string a, string b)
		{
			int[,] array = new int[a.Length + 1, b.Length + 1];
			for (int i = 0; i <= a.Length; i++)
			{
				array[i, 0] = i;
			}
			for (int j = 0; j <= b.Length; j++)
			{
				array[0, j] = j;
			}
			for (int k = 1; k <= a.Length; k++)
			{
				for (int l = 1; l <= b.Length; l++)
				{
					array[k, l] = Math.Min(Math.Min(array[k - 1, l] + 1, array[k, l - 1] + 1), array[k - 1, l - 1] + ((a[k - 1] != b[l - 1]) ? 1 : 0));
				}
			}
			return array[a.Length, b.Length];
		}
	}
}
namespace ValAgentBridge.Tools
{
	public class ControlTools
	{
		private const float HighUp = 800f;

		private const float RayStart = 1000f;

		private static readonly int SolidMask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" });

		private static FieldRef<Character, float>? _maxAirAltitude;

		private static readonly object KeepClearGate = new object();

		private static Timer? _keepClear;

		private static float _keepClearRadius;

		private static double _keepClearEvery;

		private static bool _keepClearBosses;

		private static int _keepClearRemoved;

		private static int _keepClearBusy;

		public static bool KeepClearOn { get; private set; }

		public static bool KeepClearChosen { get; private set; }

		private static void Log(string text)
		{
			ValAgentBridgePlugin.ModLogger.LogInfo((object)text);
		}

		private static bool TryLandingHeight(float x, float z, out float y)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			y = 0f;
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			foreach (RaycastHit item in from h in Physics.RaycastAll(new Vector3(x, 1000f, z), Vector3.down, 1100f, SolidMask, (QueryTriggerInteraction)1)
				orderby ((RaycastHit)(ref h)).distance
				select h)
			{
				RaycastHit current = item;
				if (!((Object)(object)((RaycastHit)(ref current)).collider.attachedRigidbody != (Object)null) && !((Object)(object)((Component)((RaycastHit)(ref current)).collider).GetComponentInParent<TreeBase>() != (Object)null))
				{
					y = Mathf.Max(((RaycastHit)(ref current)).point.y, instance.m_waterLevel) + 0.5f;
					return true;
				}
			}
			return false;
		}

		[Tool("player/teleport", Description = "(cheat) Teleport the player to a world position. Without a height the player lands on the highest surface there: ground, a rock, a roof (not tree tops), or the water surface; give the height to land inside a building. Waits until the teleport has finished (far ones load the area first) and returns where the player ended up.")]
		public object Teleport([ToolParameter(Description = "X coordinate")] float x, [ToolParameter(Description = "Z coordinate")] float z, [ToolParameter(Description = "Height; leave out to land on top of whatever is there")] float y = float.NaN)
		{
			bool landLater = false;
			int abandoned = 0;
			string text;
			try
			{
				text = MainThreadDispatcher.Run(delegate
				{
					//IL_0030: Unknown result type (might be due to invalid IL or missing references)
					//IL_0035: Unknown result type (might be due to invalid IL or missing references)
					//IL_0088: Unknown result type (might be due to invalid IL or missing references)
					//IL_0093: Unknown result type (might be due to invalid IL or missing references)
					//IL_0099: Unknown result type (might be due to invalid IL or missing references)
					//IL_00af: Unknown result type (might be due to invalid IL or missing references)
					//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
					//IL_00cd: 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)
					//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
					//IL_010a: Unknown result type (might be due to invalid IL or missing references)
					if (Interlocked.CompareExchange(ref abandoned, 0, 0) != 0)
					{
						return "abandoned";
					}
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						return "No local player (not in a world).";
					}
					Vector3 position = ((Component)localPlayer).transform.position;
					float y2 = y;
					if (float.IsNaN(y) && !TryLandingHeight(x, z, out y2))
					{
						y2 = 800f;
						landLater = true;
					}
					Vector3 val = default(Vector3);
					((Vector3)(ref val))..ctor(x, y2, z);
					bool flag = landLater || Vector3.Distance(new Vector3(position.x, 0f, position.z), new Vector3(x, 0f, z)) > 64f;
					if (!((Character)localPlayer).TeleportTo(val, ((Component)localPlayer).transform.rotation, flag))
					{
						return "The game refused (already teleporting, or within 2 s of the last teleport). Try again in a moment.";
					}
					Log(string.Format("player/teleport: ({0:F0}, {1:F0}, {2:F0}) -> ({3:F0}, {4}, {5:F0})", position.x, position.y, position.z, x, landLater ? "ground" : y2.ToString("F0"), z));
					return (string)null;
				}, 15000);
			}
			catch (TimeoutException)
			{
				Interlocked.Exchange(ref abandoned, 1);
				text = "The game's main thread did not answer in time (loading screen or paused?); the teleport was cancelled.";
			}
			catch (Exception ex2)
			{
				text = ex2.GetBaseException().Message;
			}
			if (text != null)
			{
				return new
				{
					success = false,
					error = text
				};
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			int landings = 0;
			while (stopwatch.Elapsed.TotalSeconds < 40.0)
			{
				Thread.Sleep(500);
				object obj;
				try
				{
					obj = MainThreadDispatcher.Run(delegate
					{
						//IL_0056: Unknown result type (might be due to invalid IL or missing references)
						//IL_0099: Unknown result type (might be due to invalid IL or missing references)
						//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
						//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
						Player localPlayer = Player.m_localPlayer;
						if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsTeleporting())
						{
							return (object)null;
						}
						if ((Object)(object)localPlayer != (Object)null && landLater && landings < 3 && TryLandingHeight(x, z, out var y2) && ((Component)localPlayer).transform.position.y - y2 > 2f)
						{
							if (_maxAirAltitude == null)
							{
								_maxAirAltitude = AccessTools.FieldRefAccess<Character, float>("m_maxAirAltitude");
							}
							Vector3 position = default(Vector3);
							((Vector3)(ref position))..ctor(x, y2, z);
							((Component)localPlayer).transform.position = position;
							Rigidbody component = ((Component)localPlayer).GetComponent<Rigidbody>();
							if ((Object)(object)component != (Object)null)
							{
								component.position = position;
								component.linearVelocity = Vector3.zero;
							}
							_maxAirAltitude.Invoke((Character)(object)localPlayer) = y2;
							landings++;
							return (object)null;
						}
						return Pos(localPlayer) ?? "no player";
					}, 5000);
				}
				catch (TimeoutException)
				{
					continue;
				}
				catch (Exception ex4)
				{
					return new
					{
						success = false,
						error = "Teleported, but landing failed: " + ex4.GetBaseException().Message,
						position = (object)null
					};
				}
				if (obj != null)
				{
					return new
					{
						success = true,
						position = obj,
						seconds = Math.Round(stopwatch.Elapsed.TotalSeconds, 1)
					};
				}
			}
			return new
			{
				success = false,
				error = "Still teleporting after 40 s (area slow to load). Check world/status."
			};
		}

		[Tool("world/clear_hostiles", Description = "(cheat) Remove creatures hostile to the player within a radius, so a test isn't interrupted by attacks. Leaves the player untouched (no god mode). Never removes players or tamed animals; bosses only with includeBosses. Returns what was removed.")]
		public object ClearHostiles([ToolParameter(Description = "Radius in metres around the player (default 50, max 200)")] float radius = 50f, [ToolParameter(Description = "Also remove bosses (default false)")] bool includeBosses = false)
		{
			return ClearNow(radius, includeBosses);
		}

		public static object KeepClearState()
		{
			lock (KeepClearGate)
			{
				return KeepClearOn ? new
				{
					on = true,
					radius = _keepClearRadius,
					everySeconds = _keepClearEvery,
					includeBosses = _keepClearBosses,
					removedSoFar = _keepClearRemoved
				} : new
				{
					on = false,
					radius = 0f,
					everySeconds = 0.0,
					includeBosses = false,
					removedSoFar = _keepClearRemoved
				};
			}
		}

		[Tool("world/keep_clear", Description = "(cheat) Removes creatures hostile to the player every few seconds, between calls and test scripts, so one can't kill the character mid-test. Off by default. At the start of testing, offer it to the owner and turn it on only if they want it; leave it off when the test is about hostile creatures (spawning, combat, AI, raids) or measures performance. Same rules as world/clear_hostiles. Returns the state.")]
		public object KeepClear([ToolParameter(Description = "true = keep clearing, false = stop")] bool on, [ToolParameter(Description = "Radius in metres around the player (default 50, max 200)")] float radius = 50f, [ToolParameter(Description = "Seconds between clearings (default 5, 2 to 600)")] double every = 5.0, [ToolParameter(Description = "Also remove bosses (default false)")] bool includeBosses = false)
		{
			lock (KeepClearGate)
			{
				KeepClearChosen = true;
				Switch(on, radius, every, includeBosses);
			}
			return new
			{
				success = true,
				keepClear = KeepClearState()
			};
		}

		private static void Switch(bool on, float radius, double every, bool includeBosses)
		{
			_keepClear?.Dispose();
			_keepClear = null;
			KeepClearOn = on;
			if (on)
			{
				_keepClearRadius = Mathf.Clamp(radius, 1f, 200f);
				_keepClearEvery = Math.Max(2.0, Math.Min(600.0, every));
				_keepClearBosses = includeBosses;
				TimeSpan period = TimeSpan.FromSeconds(_keepClearEvery);
				_keepClear = new Timer(delegate
				{
					KeepClearTick();
				}, null, TimeSpan.Zero, period);
			}
			Log(on ? $"world/keep_clear: on, hostile creatures within {_keepClearRadius:F0} m are removed every {_keepClearEvery:F0} s" : "world/keep_clear: off");
		}

		private static void KeepClearTick()
		{
			if (Interlocked.Exchange(ref _keepClearBusy, 1) == 1)
			{
				return;
			}
			try
			{
				KeepClearOnce();
			}
			finally
			{
				Interlocked.Exchange(ref _keepClearBusy, 0);
			}
		}

		private static void KeepClearOnce()
		{
			float radius;
			bool bosses;
			lock (KeepClearGate)
			{
				if (!KeepClearOn)
				{
					return;
				}
				radius = _keepClearRadius;
				bosses = _keepClearBosses;
			}
			try
			{
				int num = MainThreadDispatcher.Run(() => (!((Object)(object)Player.m_localPlayer == (Object)null)) ? ClearOnMainThread(radius, bosses).total : 0, 5000);
				if (num > 0)
				{
					lock (KeepClearGate)
					{
						_keepClearRemoved += num;
						return;
					}
				}
			}
			catch (Exception)
			{
			}
		}

		private static object ClearNow(float radius, bool includeBosses)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				var (removed, kinds, bossesLeft) = ClearOnMainThread(radius, includeBosses);
				return new
				{
					success = true,
					removed = removed,
					kinds = kinds,
					bossesLeft = bossesLeft
				};
			});
		}

		private static (int total, Dictionary<string, int> kinds, int bossesLeft) ClearOnMainThread(float radius, bool includeBosses)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			int num = 0;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return (total: 0, kinds: dictionary, bossesLeft: 0);
			}
			List<Character> list = new List<Character>();
			Character.GetCharactersInRange(((Component)localPlayer).transform.position, Mathf.Clamp(radius, 1f, 200f), list);
			foreach (Character item in list)
			{
				if (!((Object)(object)item == (Object)null) && !item.IsPlayer() && !item.IsTamed() && BaseAI.IsEnemy((Character)(object)localPlayer, item))
				{
					if (item.IsBoss() && !includeBosses)
					{
						num++;
						continue;
					}
					string prefabName = Utils.GetPrefabName(((Component)item).gameObject);
					dictionary[prefabName] = ((!dictionary.TryGetValue(prefabName, out var value)) ? 1 : (value + 1));
					ZNetScene.instance.Destroy(((Component)item).gameObject);
				}
			}
			int num2 = dictionary.Values.Sum();
			if (num2 > 0)
			{
				Log(string.Format("world/clear_hostiles: removed {0} within {1:F0} m ({2})", num2, radius, string.Join(", ", dictionary.Select((KeyValuePair<string, int> kv) => $"{kv.Value} {kv.Key}"))));
			}
			return (total: num2, kinds: dictionary, bossesLeft: num);
		}

		[Tool("player/set_protection", Description = "Keep the player safe while a test runs: god = takes no damage, ghost = creatures ignore the player. Switched directly, not through the console, so the character is not marked as having used cheats. Leave an argument out to keep it as it is; returns the state before and after. Not saved: both are off again after a restart.")]
		public object SetProtection([ToolParameter(Description = "No damage (true/false; leave out to keep)")] string god = "", [ToolParameter(Description = "Creatures ignore the player (true/false; leave out to keep)")] string ghost = "")
		{
			bool? wantGod = Flag(god);
			bool? wantGhost = Flag(ghost);
			if ((god.Length > 0 && !wantGod.HasValue) || (ghost.Length > 0 && !wantGhost.HasValue))
			{
				return new
				{
					success = false,
					error = "god and ghost take true or false."
				};
			}
			return MainThreadDispatcher.RunTool(delegate
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				var before = new
				{
					god = ((Character)localPlayer).InGodMode(),
					ghost = ((Character)localPlayer).InGhostMode()
				};
				if (wantGod.HasValue)
				{
					localPlayer.SetGodMode(wantGod.Value);
				}
				if (wantGhost.HasValue)
				{
					localPlayer.SetGhostMode(wantGhost.Value);
				}
				var anon = new
				{
					god = ((Character)localPlayer).InGodMode(),
					ghost = ((Character)localPlayer).InGhostMode()
				};
				if (wantGod.HasValue || wantGhost.HasValue)
				{
					Log($"player/set_protection: god {anon.god}, ghost {anon.ghost}");
				}
				return new
				{
					success = true,
					before = before,
					after = anon
				};
			});
		}

		private static bool? Flag(string text)
		{
			switch (text.Trim().ToLowerInvariant())
			{
			case "on":
			case "1":
			case "yes":
			case "true":
				return true;
			case "no":
			case "0":
			case "off":
			case "false":
				return false;
			default:
				return null;
			}
		}

		[Tool("world/find_locations", Description = "Find generated places (trader, boss altars, dungeons, villages, ...) nearest the player. With no name: lists the place types in this world and how many of each. Names are prefab names, e.g. Vendor_BlackForest, Eikthyrnir, Crypt2, StartTemple; a part of the name matches.")]
		public object FindLocations([ToolParameter(Description = "Part of the place name (empty = list the types)")] string name = "", [ToolParameter(Description = "How many to return (default 5)")] int max = 5)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)ZoneSystem.instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				List<LocationInstance> list = ZoneSystem.instance.GetLocationList().ToList();
				if (string.IsNullOrWhiteSpace(name))
				{
					var types = (from g in list.GroupBy(NameOf)
						orderby g.Key
						select new
						{
							name = g.Key,
							count = g.Count()
						}).ToList();
					return new
					{
						success = true,
						total = list.Count,
						types = types
					};
				}
				Vector3 from = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero);
				var list2 = (from l in list
					where NameOf(l).IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0
					select new
					{
						l = l,
						dist = Vector2.Distance(new Vector2(l.m_position.x, l.m_position.z), new Vector2(@from.x, @from.z))
					} into m
					orderby m.dist
					select m).ToList();
				return (list2.Count == 0) ? ((object)new
				{
					success = false,
					error = "No place matching '" + name + "'. Call with no name to list the types."
				}) : ((object)new
				{
					success = true,
					found = list2.Count,
					nearest = (from m in list2.Take(Math.Max(1, max))
						select new
						{
							name = NameOf(m.l),
							position = Vec(m.l.m_position),
							distance = Math.Round(m.dist),
							biome = ((object)Unsafe.As<Biome, Biome>(ref m.l.m_location?.m_biome)/*cast due to .constrained prefix*/).ToString(),
							placed = m.l.m_placed
						}).ToList()
				});
			});
			static string NameOf(LocationInstance l)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				return l.m_location?.m_prefabName ?? l.m_location?.m_name ?? "?";
			}
		}

		[Tool("world/spawn_creature", Description = "(cheat) Spawn creatures (prefab name, e.g. Greyling, Troll, Boar) in front of the player. Returns their instanceIds; remove them again with world/remove_objects.")]
		public object SpawnCreature([ToolParameter(Description = "Creature prefab name (znetscene/search_prefabs to look one up)")] string name, [ToolParameter(Description = "How many (default 1, max 20)")] int amount = 1, [ToolParameter(Description = "Star level: 1 = normal, 2 = one star, 3 = two stars")] int level = 1, [ToolParameter(Description = "Distance in front of the player in metres (default 5)")] float distance = 5f, [ToolParameter(Description = "Spawn tamed (default false)")] bool tamed = false)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_0117: Unknown result type (might be due to invalid IL or missing references)
				//IL_0118: Unknown result type (might be due to invalid IL or missing references)
				//IL_011a: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0153: Unknown result type (might be due to invalid IL or missing references)
				//IL_015b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0160: Unknown result type (might be due to invalid IL or missing references)
				//IL_0165: Unknown result type (might be due to invalid IL or missing references)
				//IL_0133: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(name);
				if ((Object)(object)prefab == (Object)null)
				{
					return new
					{
						success = false,
						error = "No prefab '" + name + "'. Use znetscene/search_prefabs."
					};
				}
				if ((Object)(object)prefab.GetComponent<Character>() == (Object)null)
				{
					return new
					{
						success = false,
						error = "'" + name + "' is not a creature. For items use player/give_item."
					};
				}
				amount = Mathf.Clamp(amount, 1, 20);
				Vector3 val = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * distance;
				List<object> list = new List<object>();
				float num = default(float);
				for (int i = 0; i < amount; i++)
				{
					Vector3 val2 = ((amount == 1) ? Vector3.zero : (Quaternion.Euler(0f, 360f * (float)i / (float)amount, 0f) * Vector3.forward * 1.5f));
					Vector3 val3 = val + val2;
					if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val3, ref num, 1000))
					{
						val3.y = num + 0.2f;
					}
					GameObject val4 = Object.Instantiate<GameObject>(prefab, val3, Quaternion.LookRotation(-((Component)localPlayer).transform.forward));
					Character component = val4.GetComponent<Character>();
					if (level > 1)
					{
						component.SetLevel(Mathf.Clamp(level, 1, 10));
					}
					if (tamed)
					{
						component.SetTamed(true);
					}
					list.Add(new
					{
						instanceId = ((Object)val4).GetInstanceID(),
						position = Vec(val3)
					});
				}
				Log(string.Format("world/spawn_creature: {0} x {1} (level {2}{3})", amount, name, level, tamed ? ", tamed" : ""));
				return new
				{
					success = true,
					name = name,
					level = level,
					spawned = list
				};
			});
		}

		[Tool("world/remove_objects", Description = "(cheat) Remove objects from the world by instanceId (creatures spawned for a test, dropped items, ...). Never removes the player.")]
		public object RemoveObjects([ToolParameter(Description = "instanceIds as a JSON array, e.g. [123, 456]")] string ids)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				List<int> list;
				try
				{
					list = JsonConvert.DeserializeObject<List<int>>(ids) ?? new List<int>();
				}
				catch (Exception ex)
				{
					return new
					{
						success = false,
						error = "ids is not a JSON array of numbers: " + ex.Message
					};
				}
				HashSet<int> hashSet = new HashSet<int>(list);
				int num = 0;
				ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
				foreach (ZNetView val in array)
				{
					if (!((Object)(object)val == (Object)null) && hashSet.Contains(((Object)((Component)val).gameObject).GetInstanceID()) && !((Object)(object)((Component)val).GetComponent<Player>() != (Object)null))
					{
						ZNetScene.instance.Destroy(((Component)val).gameObject);
						num++;
					}
				}
				Log($"world/remove_objects: removed {num} of {list.Count}");
				return new
				{
					success = true,
					removed = num,
					notFound = list.Count - num
				};
			});
		}

		[Tool("env/status", Description = "Time of day (hour 0-24, day number), current weather, whether time or weather are pinned, and the weather names that can be set.")]
		public object EnvStatus()
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				EnvMan instance = EnvMan.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				EnvSetup currentEnvironment = instance.GetCurrentEnvironment();
				return new
				{
					success = true,
					hour = Math.Round(instance.GetDayFraction() * 24f, 2),
					day = instance.GetDay(),
					night = EnvMan.IsNight(),
					weather = currentEnvironment?.m_name,
					timePinned = instance.m_debugTimeOfDay,
					weatherPinned = (string.IsNullOrEmpty(instance.m_debugEnv) ? null : instance.m_debugEnv),
					weathers = (from n in instance.m_environments.Select((EnvSetup e) => e.m_name).Distinct()
						orderby n
						select n).ToList()
				};
			});
		}

		[Tool("env/set_time", Description = "(cheat) Set the time of day. By default the clock jumps forward to that hour and keeps running (like sleeping). pin=true holds it there for repeatable tests (shown time only; pin=false with hour -1 releases it).")]
		public object SetTime([ToolParameter(Description = "Hour 0-24 (6 = sunrise, 12 = noon, 22 = night); -1 with pin=false just releases a pinned time")] float hour, [ToolParameter(Description = "Hold the time there (default false)")] bool pin = false)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				EnvMan instance = EnvMan.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				if (hour < 0f)
				{
					instance.m_debugTimeOfDay = false;
					Log("env/set_time: time released");
					return new
					{
						success = true,
						pinned = false,
						hour = Math.Round(instance.GetDayFraction() * 24f, 2)
					};
				}
				float num = Mathf.Repeat(hour, 24f) / 24f;
				if (pin)
				{
					instance.m_debugTimeOfDay = true;
					instance.m_debugTime = num;
				}
				else
				{
					instance.m_debugTimeOfDay = false;
					double num2 = instance.m_dayLengthSec;
					double timeSeconds = ZNet.instance.GetTimeSeconds();
					double num3 = (double)instance.GetDay(timeSeconds) * num2 + RawFraction(num) * num2;
					if (num3 <= timeSeconds)
					{
						num3 += num2;
					}
					ZNet.instance.SetNetTime(num3);
				}
				Log(string.Format("env/set_time: {0:F1}h{1}", hour, pin ? " (pinned)" : ""));
				return new
				{
					success = true,
					pinned = pin,
					hour = Math.Round(num * 24f, 2)
				};
			});
		}

		private static double RawFraction(float shown)
		{
			if (shown >= 0.25f && shown <= 0.75f)
			{
				return 0.15 + ((double)shown - 0.25) / 0.5 * 0.7;
			}
			if (shown < 0.25f)
			{
				return (double)shown / 0.25 * 0.15;
			}
			return 0.85 + ((double)shown - 0.75) / 0.25 * 0.15;
		}

		[Tool("env/set_weather", Description = "(cheat) Force a weather (name from env/status, e.g. Clear, Rain, ThunderStorm, Snow, Misty). Empty name goes back to normal weather.")]
		public object SetWeather([ToolParameter(Description = "Weather name; empty = normal weather again")] string name = "")
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				EnvMan instance = EnvMan.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				if (name.Length > 0)
				{
					EnvSetup val = ((IEnumerable<EnvSetup>)instance.m_environments).FirstOrDefault((Func<EnvSetup, bool>)((EnvSetup e) => string.Equals(e.m_name, name, StringComparison.OrdinalIgnoreCase)));
					if (val == null)
					{
						return new
						{
							success = false,
							error = "No weather '" + name + "'. Have: " + string.Join(", ", instance.m_environments.Select((EnvSetup e) => e.m_name).Distinct())
						};
					}
					name = val.m_name;
				}
				instance.m_debugEnv = name;
				Log("env/set_weather: " + ((name.Length == 0) ? "normal" : name));
				return new
				{
					success = true,
					weather = ((name.Length == 0) ? "normal" : name)
				};
			});
		}

		[Tool("world/save", Description = "Save the world and characters now (like the 'save' console command). Returns once the save has been started; the game shows 'World saved' when done.")]
		public object Save()
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				ZNet.instance.SaveWorldAndPlayerProfiles();
				Log("world/save");
				return new
				{
					success = true
				};
			});
		}

		[Tool("player/look_direction", Description = "Turn the player and camera to a compass heading and tilt, e.g. to aim a screenshot. Heading: 0 = north (+Z), 90 = east (+X). Tilt: 0 = level, positive = up, negative = down.")]
		public object LookDirection([ToolParameter(Description = "Heading in degrees (0 = north, 90 = east)")] float heading, [ToolParameter(Description = "Tilt in degrees (-80 to 80, default 0)")] float tilt = 0f)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return new
					{
						success = false,
						error = "Not in a world."
					};
				}
				PlayerLook.Set(localPlayer, heading, tilt);
				return new
				{
					success = true,
					heading = heading,
					tilt = Mathf.Clamp(tilt, -80f, 80f)
				};
			});
		}

		private static object? Pos(Player? player)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				return Vec(((Component)player).transform.position);
			}
			return null;
		}

		private static object Vec(Vector3 v)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			return new
			{
				x = Math.Round(v.x, 1),
				y = Math.Round(v.y, 1),
				z = Math.Round(v.z, 1)
			};
		}
	}
	public class EditTools
	{
		[Tool("debug/set_value", Description = "Change a live field or property by path (same paths as debug/inspect), e.g. 'Player.m_localPlayer.m_maxCarryWeight' = 600. Lasts until the game or mod changes it back or the game restarts.")]
		public object SetValue([ToolParameter(Description = "Dotted path to the field or property")] string path, [ToolParameter(Description = "New value as text: number, true/false, enum name, 'x,y,z' for a Vector3, JSON for anything else")] string value)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				string error;
				Inspector.PathEnd pathEnd = Inspector.ResolvePath(path, out error);
				if (pathEnd == null)
				{
					return new
					{
						success = false,
						error = error
					};
				}
				if (!Inspector.Write(pathEnd, value, out object before, out object after, out error))
				{
					return new
					{
						success = false,
						error = error
					};
				}
				ValAgentBridgePlugin.ModLogger.LogInfo((object)$"debug/set_value: {path}: {before} -> {after}");
				return new
				{
					success = true,
					path = path,
					before = Inspector.Describe(before, 1),
					after = Inspector.Describe(after, 1)
				};
			});
		}

		[Tool("debug/call_method", Description = "Run a method on a type (static) or on the object at a path, e.g. target 'Player.m_localPlayer', method 'GetHealth', args '[]'. A Vector3 argument can be [x, y, z] or \"x,y,z\". Returns what it returned.")]
		public object CallMethod([ToolParameter(Description = "A type (for static methods) or a path to an object")] string target, [ToolParameter(Description = "Method name")] string method, [ToolParameter(Description = "Arguments as a JSON array, e.g. '[5, \"Wood\"]' (default none)")] string args = "[]", [ToolParameter(Description = "Levels of fields to show in the result (default 1)")] int depth = 1)
		{
			return MainThreadDispatcher.RunTool(delegate
			{
				JArray argList;
				try
				{
					argList = JArray.Parse(string.IsNullOrWhiteSpace(args) ? "[]" : args);
				}
				catch (Exception ex)
				{
					return new
					{
						success = false,
						error = "args is not a JSON array: " + ex.Message
					};
				}
				string error;
				Inspector.PathEnd pathEnd = Inspector.ResolvePath(target, out error);
				if (pathEnd == null)
				{
					return new
					{
						success = false,
						error = error
					};
				}
				object instance = null;
				Type type = pathEnd.OwnerType;
				if (!pathEnd.IsTypeOnly)
				{
					if (!Inspector.Read(pathEnd, out instance, out error))
					{
						return new
						{
							success = false,
							error = error
						};
					}
					if (instance != null)
					{
						object obj = instance;
						Object val = (Object)((obj is Object) ? obj : null);
						if (val == null || !(val == (Object)null))
						{
							type = instance.GetType();
							goto IL_00e6;
						}
					}
					return new
					{
						success = false,
						error = "'" + target + "' is null"
					};
				}
				goto IL_00e6;
				IL_00e6:
				List<MethodInfo> list = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
					where m.Name == method && m.GetParameters().Length == ((JContainer)argList).Count && !m.ContainsGenericParameters
					where instance != null || m.IsStatic
					select m).ToList();
				if (list.Count == 0)
				{
					List<string> list2 = (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
						where m.Name == method
						select m.Name + "(" + string.Join(", ", from p in m.GetParameters()
							select p.ParameterType.Name + " " + p.Name) + ")").Distinct().ToList();
					return new
					{
						success = false,
						error = ((list2.Count == 0) ? (type.Name + " has no method '" + method + "'") : string.Format("No overload takes {0} argument(s). Have: {1}", ((JContainer)argList).Count, string.Join("; ", list2)))
					};
				}
				foreach (MethodInfo item in list)
				{
					object[] parameters;
					try
					{
						parameters = item.GetParameters().Select((ParameterInfo p, int i) => ConvertArg(argList[i], p.ParameterType)).ToArray();
					}
					catch
					{
						continue;
					}
					try
					{
						object value = item.Invoke(item.IsStatic ? null : instance, parameters);
						ValAgentBridgePlugin.ModLogger.LogInfo((object)("debug/call_method: " + type.Name + "." + item.Name + "(" + args + ")"));
						return new
						{
							success = true,
							method = type.Name + "." + item.Name,
							returned = ((item.ReturnType == typeof(void)) ? null : Inspector.Describe(value, Mathf.Clamp(depth, 0, 4)))
						};
					}
					catch (Exception ex2)
					{
						Exception baseException = ex2.GetBaseException();
						return new
						{
							success = false,
							error = type.Name + "." + item.Name + " threw " + baseException.GetType().Name + ": " + baseException.Message
						};
					}
				}
				return new
				{
					success = false,
					error = "The arguments don't fit any overload's parameter types."
				};
			});
		}

		private static object? ConvertArg(JToken token, Type type)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			if ((int)token.Type == 10)
			{
				return null;
			}
			if ((int)token.Type == 8 && type != typeof(string))
			{
				return Inspector.ConvertText(Extensions.Value<string>((IEnumerable<JToken>)token), type);
			}
			JArray val = (JArray)(object)((token is JArray) ? token : null);
			if (val != null && (type == typeof(Vector3) || type == typeof(Vector2)))
			{
				return Inspector.ConvertText(string.Join(",", ((IEnumerable<JToken>)val).Select((JToken n) => ((object)n).ToString())), type);
			}
			return token.ToObject(type);
		}
	}
	public class InputTools
	{
		private sealed class Simulated
		{
			public int down;

			public int up;
		}

		private static readonly Dictionary<KeyCode, Simulated> Keys = new Dictionary<KeyCode, Simulated>();

		private static bool _patched;

		private static string? _patchProblem;

		[Tool("input/press_key", Description = "Press a key (with optional modifiers) for a moment, as if typed: F11, Escape, Tab, Alpha1 / 1, LeftShift, ... Reaches the game's controls and mod hotkeys set up through BepInEx (most mods); not mods that read UnityEngine.Input directly. Returns once the key is released.")]
		public object PressKey([ToolParameter(Description = "Key name (Unity KeyCode, e.g. F11, Escape, Return, A, Alpha1 or 1)")] string key, [ToolParameter(Description = "Modifiers held with it, comma separated: ctrl, shift, alt (or KeyCode names)")] string modifiers = "", [ToolParameter(Description = "Frames to hold the key (default 2)")] int holdFrames = 2)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected I4, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!TryKey(key, out var key2))
			{
				return new
				{
					success = false,
					error = "Unknown key '" + key + "'. Use Unity KeyCode names, e.g. F11, Escape, Return, A, Alpha1."
				};
			}
			List<KeyCode> mods = new List<KeyCode>();
			string[] array = modifiers.Split(new char[3] { ',', '+', ' ' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				if (!TryKey(text, out var key3))
				{
					return new
					{
						success = false,
						error = "Unknown modifier '" + text + "'. Use ctrl, shift, alt or KeyCode names."
					};
				}
				mods.Add(key3);
			}
			List<KeyCode> all = mods.Concat((IEnumerable<KeyCode>)(object)new KeyCode[1] { (KeyCode)(int)key2 }).ToList();
			holdFrames = Mathf.Clamp(holdFrames, 1, 600);
			int num;
			bool gameControls;
			try
			{
				(num, gameControls) = MainThreadDispatcher.Run(delegate
				{
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					//IL_0040: Unknown result type (might be due to invalid IL or missing references)
					EnsurePatched();
					int num2 = Time.frameCount + 1;
					int num3 = num2 + holdFrames;
					foreach (KeyCode item in all)
					{
						Keys[item] = new Simulated
						{
							down = (mods.Contains(item) ? (num2 - 1) : num2),
							up = num3
						};
					}
					bool flag = Keyboard.current != null;
					if (flag)
					{
						((MonoBehaviour)MainThreadDispatcher.Instance).StartCoroutine(PressInputSystem(all, num3));
					}
					return (up: num3, kb: flag);
				}, 5000);
			}
			catch (Exception ex)
			{
				return new
				{
					success = false,
					error = ex.GetBaseException().Message
				};
			}
			DateTime dateTime = DateTime.UtcNow.AddSeconds(30.0);
			while (DateTime.UtcNow < dateTime)
			{
				Thread.Sleep(20);
				try
				{
					if (MainThreadDispatcher.Run(() => Time.frameCount, 2000) > num)
					{
						break;
					}
				}
				catch (TimeoutException)
				{
				}
			}
			try
			{
				MainThreadDispatcher.Run(delegate
				{
					//IL_0010: Unknown result type (might be due

plugins/Lib.GAB.dll

Decompiled 7 hours ago
#define TRACE
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Gabp.Runtime;
using Lib.GAB.Attention;
using Lib.GAB.Events;
using Lib.GAB.Protocol;
using Lib.GAB.Server;
using Lib.GAB.Tools;
using Lib.GAB.Transport;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Lib.GAB.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("GABP Contributors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Host-side GABP server library for teams embedding tool, event, and attention surfaces into game mods, automation bridges, and product-specific runtimes.")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5+6b3e5e82a10ea12f595ee1a22f9a8489b2f76510")]
[assembly: AssemblyProduct("Lib.GAB")]
[assembly: AssemblyTitle("Lib.GAB")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/pardeike/Lib.GAB")]
[assembly: AssemblyVersion("1.0.5.0")]
namespace Lib.GAB
{
	public class GabpServerBuilder
	{
		private class GabsEnvironmentConfig
		{
			public int Port { get; set; }

			public string Token { get; set; }

			public string GameId { get; set; }
		}

		private readonly GabpServerConfig _config = new GabpServerConfig();

		public GabpServerBuilder UsePort(int port)
		{
			_config.Port = port;
			return this;
		}

		public GabpServerBuilder UsePortIfNotSet(int port)
		{
			if (_config.Port == 0)
			{
				_config.Port = port;
			}
			return this;
		}

		public GabpServerBuilder UseToken(string token)
		{
			_config.Token = token;
			return this;
		}

		public GabpServerBuilder UseAgentId(string agentId)
		{
			_config.AgentId = agentId;
			return this;
		}

		public GabpServerBuilder UseAppInfo(string name, string version)
		{
			_config.AppInfo = new AppInfo
			{
				Name = name,
				Version = version
			};
			return this;
		}

		public GabpServerBuilder EnableAttentionSupport()
		{
			_config.EnableAttentionSupport = true;
			return this;
		}

		public GabpServerBuilder UseExternalConfig(int port, string token)
		{
			_config.Port = port;
			_config.Token = token;
			return this;
		}

		public GabpServerBuilder UseExternalConfig(int port, string token, string gameId)
		{
			_config.Port = port;
			_config.Token = token;
			_config.AgentId = gameId;
			return this;
		}

		public GabpServerBuilder UseGabsEnvironmentIfAvailable()
		{
			GabsEnvironmentConfig gabsEnvironmentConfig = TryReadGabsEnvironment();
			if (gabsEnvironmentConfig != null)
			{
				_config.Port = gabsEnvironmentConfig.Port;
				_config.Token = gabsEnvironmentConfig.Token;
				if (!string.IsNullOrEmpty(gabsEnvironmentConfig.GameId))
				{
					_config.AgentId = gabsEnvironmentConfig.GameId;
				}
			}
			return this;
		}

		private static GabsEnvironmentConfig TryReadGabsEnvironment()
		{
			string environmentVariable = Environment.GetEnvironmentVariable("GABS_GAME_ID");
			string environmentVariable2 = Environment.GetEnvironmentVariable("GABP_SERVER_PORT");
			string environmentVariable3 = Environment.GetEnvironmentVariable("GABP_TOKEN");
			if (!string.IsNullOrEmpty(environmentVariable2) && !string.IsNullOrEmpty(environmentVariable3) && int.TryParse(environmentVariable2, out var result) && result > 0 && result <= 65535)
			{
				return new GabsEnvironmentConfig
				{
					Port = result,
					Token = environmentVariable3,
					GameId = environmentVariable
				};
			}
			return null;
		}

		internal static bool HasGabsConfiguration()
		{
			return TryReadGabsEnvironment() != null;
		}

		public GabpServer Build()
		{
			return new GabpServer(_config);
		}
	}
	public static class Gabp
	{
		public static GabpServerBuilder CreateServer()
		{
			return new GabpServerBuilder();
		}

		public static GabpServer CreateSimpleServer(string appName, string appVersion, int port = 0)
		{
			return CreateServer().UseAppInfo(appName, appVersion).UsePort(port).Build();
		}

		public static GabpServer CreateServerWithAssembly(string appName, string AppVersion, Assembly assembly, int port = 0)
		{
			GabpServer gabpServer = CreateSimpleServer(appName, AppVersion, port);
			gabpServer.Tools.RegisterToolsFromAssembly(assembly);
			return gabpServer;
		}

		public static GabpServer CreateServerWithInstance(string appName, string appVersion, object instance, int port = 0)
		{
			GabpServer gabpServer = CreateSimpleServer(appName, appVersion, port);
			gabpServer.Tools.RegisterToolsFromInstance(instance);
			return gabpServer;
		}

		public static GabpServer CreateServerWithInstanceAndExternalConfig(string appName, string appVersion, object instance, int port, string token)
		{
			GabpServer gabpServer = CreateServerWithExternalConfig(appName, appVersion, port, token);
			gabpServer.Tools.RegisterToolsFromInstance(instance);
			return gabpServer;
		}

		public static GabpServer CreateServerWithInstanceAndExternalConfig(string appName, string appVersion, object instance, int port, string token, string gameId)
		{
			GabpServer gabpServer = CreateServerWithExternalConfig(appName, appVersion, port, token, gameId);
			gabpServer.Tools.RegisterToolsFromInstance(instance);
			return gabpServer;
		}

		public static GabpServer CreateServerWithExternalConfig(string appName, string appVersion, int port, string token)
		{
			return CreateServer().UseAppInfo(appName, appVersion).UseExternalConfig(port, token).Build();
		}

		public static GabpServer CreateServerWithExternalConfig(string appName, string appVersion, int port, string token, string gameId)
		{
			return CreateServer().UseAppInfo(appName, appVersion).UseExternalConfig(port, token, gameId).Build();
		}

		public static GabpServer CreateGabsAwareServer(string appName, string appVersion, int fallbackPort = 0)
		{
			return CreateServer().UseAppInfo(appName, appVersion).UseGabsEnvironmentIfAvailable().UsePortIfNotSet(fallbackPort)
				.Build();
		}

		public static GabpServer CreateGabsAwareServerWithInstance(string appName, string appVersion, object instance, int fallbackPort = 0)
		{
			GabpServer gabpServer = CreateGabsAwareServer(appName, appVersion, fallbackPort);
			gabpServer.Tools.RegisterToolsFromInstance(instance);
			return gabpServer;
		}

		public static bool IsRunningUnderGabs()
		{
			return GabpServerBuilder.HasGabsConfiguration();
		}
	}
}
namespace Lib.GAB.Transport
{
	public interface IConnection : IDisposable
	{
		string Id { get; }

		bool IsConnected { get; }

		event EventHandler Disconnected;

		Task SendMessageAsync(GabpMessage message, CancellationToken cancellationToken = default(CancellationToken));
	}
	public interface ITransport : IDisposable
	{
		event EventHandler<ConnectionEstablishedEventArgs> ConnectionEstablished;

		event EventHandler<MessageReceivedEventArgs> MessageReceived;

		Task StartAsync(CancellationToken cancellationToken = default(CancellationToken));

		Task StopAsync(CancellationToken cancellationToken = default(CancellationToken));
	}
	public class ConnectionEstablishedEventArgs : EventArgs
	{
		public IConnection Connection { get; }

		public ConnectionEstablishedEventArgs(IConnection connection)
		{
			Connection = connection;
		}
	}
	public class MessageReceivedEventArgs : EventArgs
	{
		public IConnection Connection { get; }

		public GabpMessage Message { get; }

		public MessageReceivedEventArgs(IConnection connection, GabpMessage message)
		{
			Connection = connection;
			Message = message;
		}
	}
	public class TcpConnection : IConnection, IDisposable
	{
		internal readonly TcpClient _client;

		private readonly Stream _stream;

		private readonly string _id;

		private readonly CancellationTokenSource _cancellationTokenSource;

		private readonly SemaphoreSlim _sendLock;

		private bool _disposed;

		public string Id => _id;

		public bool IsConnected
		{
			get
			{
				if (_client.Connected)
				{
					return !_disposed;
				}
				return false;
			}
		}

		public event EventHandler Disconnected;

		public TcpConnection(TcpClient client)
			: this(client, client?.GetStream() ?? throw new ArgumentNullException("client"))
		{
		}

		internal TcpConnection(TcpClient client, Stream stream)
		{
			_client = client ?? throw new ArgumentNullException("client");
			_stream = stream ?? throw new ArgumentNullException("stream");
			_id = Guid.NewGuid().ToString();
			_cancellationTokenSource = new CancellationTokenSource();
			_sendLock = new SemaphoreSlim(1, 1);
		}

		public async Task SendMessageAsync(GabpMessage message, CancellationToken cancellationToken = default(CancellationToken))
		{
			if (_disposed || !IsConnected)
			{
				throw new InvalidOperationException("Connection is not active");
			}
			string s = JsonConvert.SerializeObject((object)message);
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			string s2 = $"Content-Length: {bytes.Length}\r\nContent-Type: application/json\r\n\r\n";
			byte[] bytes2 = Encoding.UTF8.GetBytes(s2);
			byte[] frameBytes = new byte[bytes2.Length + bytes.Length];
			Buffer.BlockCopy(bytes2, 0, frameBytes, 0, bytes2.Length);
			Buffer.BlockCopy(bytes, 0, frameBytes, bytes2.Length, bytes.Length);
			using CancellationTokenSource linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token);
			CancellationToken combinedToken = linkedTokenSource.Token;
			await _sendLock.WaitAsync(combinedToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				if (_disposed || !IsConnected)
				{
					throw new InvalidOperationException("Connection is not active");
				}
				await _stream.WriteAsync(frameBytes, 0, frameBytes.Length, combinedToken).ConfigureAwait(continueOnCapturedContext: false);
				await _stream.FlushAsync(combinedToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_sendLock.Release();
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_cancellationTokenSource.Cancel();
				try
				{
					this.Disconnected?.Invoke(this, EventArgs.Empty);
				}
				catch
				{
				}
				_stream?.Dispose();
				_client?.Close();
				_cancellationTokenSource?.Dispose();
			}
		}
	}
	public class TcpTransport : ITransport, IDisposable
	{
		private readonly int _port;

		private TcpListener _listener;

		private bool _disposed;

		private bool _running;

		public int Port { get; private set; }

		public event EventHandler<ConnectionEstablishedEventArgs> ConnectionEstablished;

		public event EventHandler<MessageReceivedEventArgs> MessageReceived;

		public TcpTransport(int port = 0)
		{
			_port = port;
		}

		public async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			if (_running)
			{
				throw new InvalidOperationException("Transport is already running");
			}
			_listener = new TcpListener(IPAddress.Loopback, _port);
			_listener.Start();
			Port = ((IPEndPoint)_listener.LocalEndpoint).Port;
			_running = true;
			Task.Run(async delegate
			{
				await AcceptConnectionsAsync(cancellationToken);
			}, cancellationToken);
		}

		public Task StopAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			if (!_running)
			{
				return Task.FromResult(0);
			}
			_running = false;
			_listener?.Stop();
			return Task.FromResult(0);
		}

		private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
		{
			while (_running && !cancellationToken.IsCancellationRequested)
			{
				try
				{
					TcpConnection connection = new TcpConnection(await _listener.AcceptTcpClientAsync());
					this.ConnectionEstablished?.Invoke(this, new ConnectionEstablishedEventArgs(connection));
					Task.Run(async delegate
					{
						await ReadMessagesAsync(connection, cancellationToken);
					}, cancellationToken);
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (Exception)
				{
					if (_running)
					{
						await Task.Delay(1000, cancellationToken);
					}
				}
			}
		}

		private async Task ReadMessagesAsync(TcpConnection connection, CancellationToken cancellationToken)
		{
			NetworkStream stream = connection._client.GetStream();
			byte[] buffer = new byte[8192];
			StringBuilder messageBuffer = new StringBuilder();
			try
			{
				while (connection.IsConnected && !cancellationToken.IsCancellationRequested)
				{
					int num = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
					if (num != 0)
					{
						string value = Encoding.UTF8.GetString(buffer, 0, num);
						messageBuffer.Append(value);
						await ProcessMessagesAsync(connection, messageBuffer, cancellationToken);
						continue;
					}
					break;
				}
			}
			catch (Exception)
			{
			}
			finally
			{
				connection.Dispose();
			}
		}

		private Task ProcessMessagesAsync(TcpConnection connection, StringBuilder buffer, CancellationToken cancellationToken)
		{
			while (true)
			{
				string text = buffer.ToString();
				int num = text.IndexOf("\r\n\r\n", StringComparison.Ordinal);
				if (num == -1)
				{
					break;
				}
				string text2 = text.Substring(0, num);
				int num2 = text2.IndexOf("Content-Length:", StringComparison.OrdinalIgnoreCase);
				if (num2 == -1)
				{
					break;
				}
				int num3 = num2 + "Content-Length:".Length;
				int num4 = text2.IndexOf('\r', num3);
				if (num4 == -1)
				{
					num4 = text2.IndexOf('\n', num3);
				}
				if (num4 == -1)
				{
					num4 = text2.Length;
				}
				if (!int.TryParse(text2.Substring(num3, num4 - num3).Trim(), out var result))
				{
					break;
				}
				int num5 = num + 4;
				if (text.Length < num5 + result)
				{
					break;
				}
				string json = text.Substring(num5, result);
				try
				{
					GabpMessage gabpMessage = ParseMessage(json);
					if (gabpMessage != null)
					{
						this.MessageReceived?.Invoke(this, new MessageReceivedEventArgs(connection, gabpMessage));
					}
				}
				catch (Exception)
				{
				}
				buffer.Remove(0, num5 + result);
			}
			return Task.FromResult(0);
		}

		private static GabpMessage ParseMessage(string json)
		{
			Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
			if (dictionary == null || !dictionary.ContainsKey("type"))
			{
				return null;
			}
			return dictionary["type"].ToString() switch
			{
				"request" => JsonConvert.DeserializeObject<GabpRequest>(json), 
				"response" => JsonConvert.DeserializeObject<GabpResponse>(json), 
				"event" => JsonConvert.DeserializeObject<GabpEvent>(json), 
				_ => null, 
			};
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				if (_running)
				{
					_running = false;
					_listener?.Stop();
				}
			}
		}
	}
}
namespace Lib.GAB.Tools
{
	public class ToolInfo
	{
		public string Name { get; set; } = string.Empty;

		public string Description { get; set; }

		public string Title { get; set; }

		public string ResultDescription { get; set; }

		public List<string> Tags { get; set; } = new List<string>();

		public List<ToolParameterInfo> Parameters { get; set; } = new List<ToolParameterInfo>();

		public List<ToolResponseFieldInfo> ResponseFields { get; set; } = new List<ToolResponseFieldInfo>();

		public bool RequiresAuth { get; set; } = true;
	}
	public class ToolResponseFieldInfo
	{
		public string Name { get; set; } = string.Empty;

		public string Type { get; set; } = "string";

		public string Description { get; set; }

		public bool Always { get; set; } = true;

		public bool Nullable { get; set; }
	}
	public class ToolParameterInfo
	{
		public string Name { get; set; } = string.Empty;

		public Type Type { get; set; } = typeof(object);

		public string Description { get; set; }

		public bool Required { get; set; } = true;

		public object DefaultValue { get; set; }
	}
	public interface IToolRegistry
	{
		void RegisterTool(string name, Func<object, Task<object>> handler, ToolInfo info = null);

		void RegisterToolsFromAssembly(Assembly assembly);

		void RegisterToolsFromInstance(object instance);

		bool UnregisterTool(string name);

		IList<ToolInfo> GetTools();

		bool HasTool(string name);

		Task<object> CallToolAsync(string name, object parameters = null);
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public class ToolAttribute : Attribute
	{
		public string Name { get; }

		public string Description { get; set; }

		public string Title { get; set; }

		public string ResultDescription { get; set; }

		public string[] Tags { get; set; } = Array.Empty<string>();

		public bool RequiresAuth { get; set; } = true;

		public ToolAttribute(string name)
		{
			ToolNameValidator.EnsureValid(name);
			Name = name;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
	public class ToolParameterAttribute : Attribute
	{
		public string Description { get; set; }

		public bool Required { get; set; } = true;

		public object DefaultValue { get; set; }
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
	public class ToolResponseAttribute : Attribute
	{
		public string Name { get; }

		public string Type { get; set; } = "string";

		public string Description { get; set; }

		public bool Always { get; set; } = true;

		public bool Nullable { get; set; }

		public ToolResponseAttribute(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentException("Response field name cannot be null or empty", "name");
			}
			Name = name;
		}
	}
	public static class ToolNameValidator
	{
		public const string CanonicalPattern = "^[a-z][a-z0-9_-]*(/[a-z][a-z0-9_-]*)+$";

		private static readonly Regex CanonicalToolNameRegex = new Regex("^[a-z][a-z0-9_-]*(/[a-z][a-z0-9_-]*)+$", RegexOptions.CultureInvariant);

		public static bool IsValid(string name)
		{
			if (!string.IsNullOrWhiteSpace(name))
			{
				return CanonicalToolNameRegex.IsMatch(name);
			}
			return false;
		}

		public static void EnsureValid(string name, string parameterName = "name")
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentException("Tool name cannot be null or empty", parameterName);
			}
			if (!IsValid(name))
			{
				throw new ArgumentException("Tool name '" + name + "' must be a canonical GABP tool name matching ^[a-z][a-z0-9_-]*(/[a-z][a-z0-9_-]*)+$ (for example 'inventory/get'). Dotted MCP adapter names such as 'inventory.get' are not valid GABP tool names.", parameterName);
			}
		}
	}
	internal sealed class ToolParameterBindingException : Exception
	{
		public ToolParameterBindingException(string message, Exception innerException)
			: base(message, innerException)
		{
		}
	}
	public class ToolRegistry : IToolRegistry
	{
		private class RegisteredTool
		{
			public ToolInfo Info { get; set; } = new ToolInfo();

			public Func<object, Task<object>> Handler { get; set; } = (object _) => Task.FromResult<object>(null);
		}

		private readonly ConcurrentDictionary<string, RegisteredTool> _tools = new ConcurrentDictionary<string, RegisteredTool>();

		public void RegisterTool(string name, Func<object, Task<object>> handler, ToolInfo info = null)
		{
			ToolNameValidator.EnsureValid(name);
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			ToolInfo toolInfo = info ?? new ToolInfo
			{
				Name = name
			};
			toolInfo.Name = name;
			_tools[name] = new RegisteredTool
			{
				Info = toolInfo,
				Handler = handler
			};
		}

		public void RegisterToolsFromAssembly(Assembly assembly)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException("assembly");
			}
			Type[] types = assembly.GetTypes();
			foreach (Type type in types)
			{
				RegisterToolsFromType(type, null);
			}
		}

		public void RegisterToolsFromInstance(object instance)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			RegisterToolsFromType(instance.GetType(), instance);
		}

		private void RegisterToolsFromType(Type type, object instance)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				ToolAttribute customAttribute = methodInfo.GetCustomAttribute<ToolAttribute>();
				if (customAttribute != null && (instance != null || methodInfo.IsStatic) && (instance == null || !methodInfo.IsStatic))
				{
					ToolInfo info = new ToolInfo
					{
						Name = customAttribute.Name,
						Title = customAttribute.Title,
						Description = customAttribute.Description,
						ResultDescription = customAttribute.ResultDescription,
						Tags = NormalizeTags(customAttribute.Tags),
						RequiresAuth = customAttribute.RequiresAuth,
						Parameters = GetParameterInfo(methodInfo),
						ResponseFields = GetResponseFieldInfo(methodInfo)
					};
					RegisterTool(customAttribute.Name, CreateHandler(methodInfo, instance, customAttribute.Name), info);
				}
			}
		}

		private static List<string> NormalizeTags(IEnumerable<string> tags)
		{
			if (tags == null)
			{
				return new List<string>();
			}
			return (from tag in tags
				where !string.IsNullOrWhiteSpace(tag)
				select tag.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
		}

		private List<ToolParameterInfo> GetParameterInfo(MethodInfo method)
		{
			List<ToolParameterInfo> list = new List<ToolParameterInfo>();
			ParameterInfo[] parameters = method.GetParameters();
			foreach (ParameterInfo parameterInfo in parameters)
			{
				ToolParameterAttribute customAttribute = parameterInfo.GetCustomAttribute<ToolParameterAttribute>();
				object defaultValue = GetDefaultValue(parameterInfo, customAttribute);
				list.Add(new ToolParameterInfo
				{
					Name = parameterInfo.Name,
					Type = parameterInfo.ParameterType,
					Description = customAttribute?.Description,
					Required = IsParameterRequired(parameterInfo, customAttribute),
					DefaultValue = defaultValue
				});
			}
			return list;
		}

		private static bool IsParameterRequired(ParameterInfo parameter, ToolParameterAttribute parameterAttribute)
		{
			if (parameter.HasDefaultValue)
			{
				return false;
			}
			if (parameterAttribute != null && parameterAttribute.DefaultValue != null)
			{
				return false;
			}
			return parameterAttribute?.Required ?? true;
		}

		private static object GetDefaultValue(ParameterInfo parameter, ToolParameterAttribute parameterAttribute)
		{
			if (parameter.HasDefaultValue)
			{
				return parameter.DefaultValue;
			}
			return parameterAttribute?.DefaultValue;
		}

		private List<ToolResponseFieldInfo> GetResponseFieldInfo(MethodInfo method)
		{
			List<ToolResponseFieldInfo> list = new List<ToolResponseFieldInfo>();
			foreach (ToolResponseAttribute customAttribute in method.GetCustomAttributes<ToolResponseAttribute>())
			{
				list.Add(new ToolResponseFieldInfo
				{
					Name = customAttribute.Name,
					Type = customAttribute.Type,
					Description = customAttribute.Description,
					Always = customAttribute.Always,
					Nullable = customAttribute.Nullable
				});
			}
			return list;
		}

		private Func<object, Task<object>> CreateHandler(MethodInfo method, object instance, string toolName)
		{
			return async delegate(object parameters)
			{
				try
				{
					object[] parameters2 = ConvertParameters(method, parameters, toolName);
					object obj = method.Invoke(instance, parameters2);
					if (obj is Task task)
					{
						await task;
						if (task.GetType().IsGenericType)
						{
							return task.GetType().GetProperty("Result")?.GetValue(task);
						}
						return (object)null;
					}
					return obj;
				}
				catch (ToolParameterBindingException)
				{
					throw;
				}
				catch (Exception ex2)
				{
					throw new InvalidOperationException("Error calling tool '" + method.Name + "': " + ex2.Message, ex2);
				}
			};
		}

		private object[] ConvertParameters(MethodInfo method, object parameters, string toolName)
		{
			ParameterInfo[] parameters2 = method.GetParameters();
			object[] array = new object[parameters2.Length];
			if (parameters == null)
			{
				for (int i = 0; i < parameters2.Length; i++)
				{
					array[i] = GetDefaultValue(parameters2[i], parameters2[i].GetCustomAttribute<ToolParameterAttribute>());
				}
				return array;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			dictionary = ((!(parameters is string text)) ? (JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(parameters)) ?? new Dictionary<string, object>()) : (JsonConvert.DeserializeObject<Dictionary<string, object>>(text) ?? new Dictionary<string, object>()));
			string[] array2 = parameters2.Select((ParameterInfo p) => p.Name).ToArray();
			HashSet<string> hashSet = new HashSet<string>(array2, StringComparer.Ordinal);
			foreach (string key in dictionary.Keys)
			{
				if (!hashSet.Contains(key) && array2.FirstOrDefault((string n) => n.Equals(key, StringComparison.OrdinalIgnoreCase)) == null)
				{
					Trace.TraceWarning("[ToolRegistry] Tool '" + method.Name + "': unrecognized parameter '" + key + "'. Known parameters: [" + string.Join(", ", array2) + "]");
				}
			}
			for (int num = 0; num < parameters2.Length; num++)
			{
				ParameterInfo param = parameters2[num];
				if (dictionary.ContainsKey(param.Name))
				{
					try
					{
						object value = dictionary[param.Name];
						array[num] = ConvertValue(value, param.ParameterType);
					}
					catch (Exception innerException)
					{
						throw CreateParameterBindingException(toolName, param, innerException);
					}
					continue;
				}
				string text2 = dictionary.Keys.FirstOrDefault((string k) => k.Equals(param.Name, StringComparison.OrdinalIgnoreCase));
				if (text2 != null)
				{
					Trace.TraceWarning("[ToolRegistry] Tool '" + method.Name + "': parameter '" + text2 + "' matched '" + param.Name + "' via case-insensitive fallback.");
					try
					{
						object value2 = dictionary[text2];
						array[num] = ConvertValue(value2, param.ParameterType);
					}
					catch (Exception innerException2)
					{
						throw CreateParameterBindingException(toolName, param, innerException2);
					}
				}
				else
				{
					array[num] = GetDefaultValue(param, param.GetCustomAttribute<ToolParameterAttribute>());
				}
			}
			return array;
		}

		private static ToolParameterBindingException CreateParameterBindingException(string toolName, ParameterInfo parameter, Exception innerException)
		{
			return new ToolParameterBindingException("Invalid value for parameter '" + parameter.Name + "' in tool '" + toolName + "'; expected " + parameter.ParameterType.Name + ".", innerException);
		}

		private object ConvertValue(object value, Type targetType)
		{
			if (value == null)
			{
				return null;
			}
			if (targetType.IsAssignableFrom(value.GetType()))
			{
				return value;
			}
			try
			{
				return Convert.ChangeType(value, targetType);
			}
			catch
			{
				return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), targetType);
			}
		}

		public bool UnregisterTool(string name)
		{
			RegisteredTool value;
			return _tools.TryRemove(name, out value);
		}

		public IList<ToolInfo> GetTools()
		{
			return _tools.Values.Select((RegisteredTool t) => t.Info).ToList();
		}

		public bool HasTool(string name)
		{
			return _tools.ContainsKey(name);
		}

		public async Task<object> CallToolAsync(string name, object parameters = null)
		{
			if (!_tools.TryGetValue(name, out var value))
			{
				throw new ArgumentException("Tool '" + name + "' not found", "name");
			}
			return await value.Handler(parameters);
		}
	}
}
namespace Lib.GAB.Server
{
	public class GabpServerConfig
	{
		public int Port { get; set; }

		public string Token { get; set; } = Guid.NewGuid().ToString("N");

		public string AgentId { get; set; } = "gabp-server";

		public AppInfo AppInfo { get; set; } = new AppInfo
		{
			Name = "GABP Server",
			Version = "1.0.0"
		};

		public bool EnableAttentionSupport { get; set; }
	}
	public class GabpServer : IDisposable
	{
		private class SessionInfo
		{
			public IConnection Connection { get; set; }

			public bool IsAuthenticated { get; set; }

			public string BridgeVersion { get; set; }

			public string Platform { get; set; }

			public string LaunchId { get; set; }
		}

		private readonly GabpServerConfig _config;

		private readonly ITransport _transport;

		private readonly IToolRegistry _toolRegistry;

		private readonly IEventManager _eventManager;

		private readonly IAttentionManager _attentionManager;

		private readonly ConcurrentDictionary<string, SessionInfo> _sessions = new ConcurrentDictionary<string, SessionInfo>();

		private bool _disposed;

		private static readonly IReadOnlyList<string> BaseProtocolMethods = new string[5] { "session/hello", "tools/list", "tools/call", "events/subscribe", "events/unsubscribe" };

		public int Port
		{
			get
			{
				if (!(_transport is TcpTransport tcpTransport))
				{
					return 0;
				}
				return tcpTransport.Port;
			}
		}

		public string Token => _config.Token;

		public IToolRegistry Tools => _toolRegistry;

		public IEventManager Events => _eventManager;

		public IAttentionManager Attention => _attentionManager;

		public GabpServer(GabpServerConfig config = null)
		{
			_config = config ?? new GabpServerConfig();
			_transport = new TcpTransport(_config.Port);
			_toolRegistry = new ToolRegistry();
			_eventManager = new EventManager();
			_attentionManager = new AttentionManager(_eventManager, _config.EnableAttentionSupport);
			SetupTransportEvents();
			RegisterCoreTools();
			RegisterCoreEventChannels();
		}

		public async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _transport.StartAsync(cancellationToken);
		}

		public async Task StopAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _transport.StopAsync(cancellationToken);
			DisposeActiveSessions();
		}

		private void SetupTransportEvents()
		{
			_transport.ConnectionEstablished += OnConnectionEstablished;
			_transport.MessageReceived += OnMessageReceived;
		}

		private void OnConnectionEstablished(object sender, ConnectionEstablishedEventArgs e)
		{
			_sessions[e.Connection.Id] = new SessionInfo
			{
				Connection = e.Connection,
				IsAuthenticated = false
			};
			e.Connection.Disconnected += delegate
			{
				_sessions.TryRemove(e.Connection.Id, out var _);
			};
		}

		private async void OnMessageReceived(object sender, MessageReceivedEventArgs e)
		{
			try
			{
				await HandleMessageAsync(e.Connection, e.Message);
			}
			catch (Exception ex)
			{
				await SendErrorResponseAsync(e.Connection, e.Message.Id, -32603, "Internal server error: " + ex.Message);
			}
		}

		private async Task HandleMessageAsync(IConnection connection, GabpMessage message)
		{
			if (!_sessions.TryGetValue(connection.Id, out var value))
			{
				return;
			}
			if (!(message is GabpRequest request))
			{
				if (!(message is GabpResponse) && message is GabpEvent)
				{
				}
			}
			else
			{
				await HandleRequestAsync(connection, value, request);
			}
		}

		private async Task HandleRequestAsync(IConnection connection, SessionInfo session, GabpRequest request)
		{
			if (request.Method == "session/hello")
			{
				await HandleSessionHelloAsync(connection, session, request);
				return;
			}
			if (!session.IsAuthenticated)
			{
				await SendErrorResponseAsync(connection, request.Id, -31001, "Session not established. Send session/hello first.");
				return;
			}
			switch (request.Method)
			{
			case "tools/list":
				await HandleToolsListAsync(connection, request);
				return;
			case "tools/call":
				await HandleToolsCallAsync(connection, request);
				return;
			case "events/subscribe":
				await HandleEventsSubscribeAsync(connection, request);
				return;
			case "events/unsubscribe":
				await HandleEventsUnsubscribeAsync(connection, request);
				return;
			case "attention/current":
				if (_attentionManager.IsEnabled)
				{
					await HandleAttentionCurrentAsync(connection, request);
					return;
				}
				break;
			case "attention/ack":
				if (_attentionManager.IsEnabled)
				{
					await HandleAttentionAckAsync(connection, request);
					return;
				}
				break;
			}
			await SendErrorResponseAsync(connection, request.Id, -32601, "Method '" + request.Method + "' not found");
		}

		private async Task HandleSessionHelloAsync(IConnection connection, SessionInfo session, GabpRequest request)
		{
			try
			{
				SessionHelloParams val = GabpJson.Deserialize<SessionHelloParams>(JsonConvert.SerializeObject(request.Params));
				if (val == null || val.Token != _config.Token)
				{
					await SendErrorResponseAsync(connection, request.Id, -31000, "Invalid authentication token");
					return;
				}
				session.IsAuthenticated = true;
				session.BridgeVersion = val.BridgeVersion;
				session.Platform = val.Platform;
				session.LaunchId = val.LaunchId;
				SessionWelcomeResult result = new SessionWelcomeResult
				{
					AgentId = _config.AgentId,
					App = new GabpAppInfo
					{
						Name = _config.AppInfo.Name,
						Version = _config.AppInfo.Version
					},
					Capabilities = new GabpCapabilities
					{
						Methods = GetSupportedProtocolMethods(),
						Events = _eventManager.GetAvailableChannels(),
						Resources = new List<string>()
					},
					SchemaVersion = "1.0"
				};
				await SendResponseAsync(connection, request.Id, result);
			}
			catch (Exception ex)
			{
				await SendErrorResponseAsync(connection, request.Id, -32602, "Invalid session/hello parameters: " + ex.Message);
			}
		}

		private async Task HandleToolsListAsync(IConnection connection, GabpRequest request)
		{
			List<Dictionary<string, object>> tools = _toolRegistry.GetTools().Select(BuildToolDescriptor).ToList();
			await SendResponseAsync(connection, request.Id, new { tools });
		}

		private static Dictionary<string, object> BuildToolDescriptor(ToolInfo tool)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["name"] = tool.Name,
				["title"] = (string.IsNullOrWhiteSpace(tool.Title) ? BuildToolTitle(tool.Name) : tool.Title),
				["description"] = tool.Description,
				["inputSchema"] = BuildInputSchema(tool.Parameters),
				["parameters"] = tool.Parameters.Select((ToolParameterInfo parameter) => new
				{
					name = parameter.Name,
					type = parameter.Type.Name,
					description = parameter.Description,
					required = parameter.Required,
					defaultValue = parameter.DefaultValue
				}).ToList(),
				["outputSchema"] = BuildOutputSchema(tool.ResultDescription, tool.ResponseFields),
				["requiresAuth"] = tool.RequiresAuth
			};
			if (tool.Tags != null && tool.Tags.Count > 0)
			{
				dictionary["tags"] = tool.Tags;
			}
			return dictionary;
		}

		private static Dictionary<string, object> BuildInputSchema(List<ToolParameterInfo> parameters)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			List<string> list = new List<string>();
			foreach (ToolParameterInfo parameter in parameters)
			{
				Dictionary<string, object> dictionary2 = BuildParameterSchema(parameter.Type);
				if (!string.IsNullOrWhiteSpace(parameter.Description))
				{
					dictionary2["description"] = parameter.Description;
				}
				if (parameter.DefaultValue != null)
				{
					dictionary2["default"] = parameter.DefaultValue;
				}
				dictionary[parameter.Name] = dictionary2;
				if (parameter.Required)
				{
					list.Add(parameter.Name);
				}
			}
			Dictionary<string, object> dictionary3 = new Dictionary<string, object>
			{
				["type"] = "object",
				["properties"] = dictionary,
				["additionalProperties"] = false
			};
			if (list.Count > 0)
			{
				dictionary3["required"] = list;
			}
			return dictionary3;
		}

		private static Dictionary<string, object> BuildParameterSchema(Type type)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object> { ["type"] = MapTypeToJsonSchemaType(type) };
			if (TryGetDictionaryValueType(type, out var valueType))
			{
				dictionary["additionalProperties"] = ((valueType == typeof(object)) ? new Dictionary<string, object>() : new Dictionary<string, object> { ["type"] = MapTypeToJsonSchemaType(valueType) });
			}
			return dictionary;
		}

		private static Dictionary<string, object> BuildOutputSchema(string resultDescription, List<ToolResponseFieldInfo> fields)
		{
			if (fields.Count == 0 && string.IsNullOrWhiteSpace(resultDescription))
			{
				return new Dictionary<string, object>();
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			if (!string.IsNullOrWhiteSpace(resultDescription))
			{
				dictionary["description"] = resultDescription;
			}
			if (fields.Count == 0)
			{
				return dictionary;
			}
			Dictionary<string, object> dictionary2 = new Dictionary<string, object>();
			List<string> list = new List<string>();
			foreach (ToolResponseFieldInfo field in fields)
			{
				Dictionary<string, object> dictionary3 = new Dictionary<string, object> { ["type"] = field.Type };
				if (field.Nullable)
				{
					dictionary3["nullable"] = true;
				}
				if (!string.IsNullOrEmpty(field.Description))
				{
					dictionary3["description"] = field.Description;
				}
				dictionary2[field.Name] = dictionary3;
				if (field.Always)
				{
					list.Add(field.Name);
				}
			}
			dictionary["type"] = "object";
			dictionary["properties"] = dictionary2;
			if (list.Count > 0)
			{
				dictionary["required"] = list;
			}
			return dictionary;
		}

		private static string BuildToolTitle(string toolName)
		{
			List<string> list = (from part in toolName.Split(new char[3] { '/', '_', '-' }, StringSplitOptions.RemoveEmptyEntries)
				select CultureInfo.InvariantCulture.TextInfo.ToTitleCase(part)).ToList();
			if (list.Count != 0)
			{
				return string.Join(" ", list);
			}
			return toolName;
		}

		private static string MapTypeToJsonSchemaType(Type type)
		{
			Type type2 = Nullable.GetUnderlyingType(type) ?? type;
			if (type2 == typeof(string) || type2 == typeof(char) || type2 == typeof(Guid))
			{
				return "string";
			}
			if (type2 == typeof(bool))
			{
				return "boolean";
			}
			if (type2 == typeof(byte) || type2 == typeof(sbyte) || type2 == typeof(short) || type2 == typeof(ushort) || type2 == typeof(int) || type2 == typeof(uint) || type2 == typeof(long) || type2 == typeof(ulong))
			{
				return "integer";
			}
			if (type2 == typeof(float) || type2 == typeof(double) || type2 == typeof(decimal))
			{
				return "number";
			}
			if (TryGetDictionaryValueType(type2, out var _))
			{
				return "object";
			}
			if (type2 != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type2))
			{
				return "array";
			}
			return "object";
		}

		private static bool TryGetDictionaryValueType(Type type, out Type valueType)
		{
			Type type2 = Nullable.GetUnderlyingType(type) ?? type;
			Type type3 = new Type[1] { type2 }.Concat(type2.GetInterfaces()).FirstOrDefault((Type candidate) => candidate.IsGenericType && (candidate.GetGenericTypeDefinition() == typeof(IDictionary<, >) || candidate.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<, >)));
			if (type3 != null)
			{
				valueType = type3.GetGenericArguments()[1];
				return true;
			}
			if (typeof(IDictionary).IsAssignableFrom(type2))
			{
				valueType = typeof(object);
				return true;
			}
			valueType = null;
			return false;
		}

		private async Task HandleToolsCallAsync(IConnection connection, GabpRequest request)
		{
			try
			{
				string text = JsonConvert.SerializeObject(request.Params);
				ToolsCallParams val = GabpJson.Deserialize<ToolsCallParams>(text);
				if (val == null || string.IsNullOrWhiteSpace(val.Name))
				{
					await SendErrorResponseAsync(connection, request.Id, -32602, "Missing 'name' parameter");
					return;
				}
				string name = val.Name;
				if (string.IsNullOrEmpty(name))
				{
					await SendErrorResponseAsync(connection, request.Id, -32602, "Tool name cannot be empty");
					return;
				}
				if (!_toolRegistry.HasTool(name))
				{
					await SendErrorResponseAsync(connection, request.Id, -31002, "Tool '" + name + "' not found");
					return;
				}
				object parameters = null;
				JToken val2 = default(JToken);
				if (JObject.Parse(text).TryGetValue("parameters", ref val2) && (int)val2.Type != 10)
				{
					parameters = val2;
				}
				else if (val.Arguments != null)
				{
					parameters = val.Arguments;
				}
				object result = await _toolRegistry.CallToolAsync(name, parameters);
				await SendResponseAsync(connection, request.Id, result);
			}
			catch (ToolParameterBindingException ex)
			{
				await SendErrorResponseAsync(connection, request.Id, -32602, ex.Message);
			}
			catch (Exception ex2)
			{
				await SendErrorResponseAsync(connection, request.Id, -32603, "Tool execution failed: " + ex2.Message);
			}
		}

		private async Task HandleEventsSubscribeAsync(IConnection connection, GabpRequest request)
		{
			try
			{
				Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(request.Params));
				if (dictionary == null || !dictionary.ContainsKey("channels"))
				{
					await SendErrorResponseAsync(connection, request.Id, -32602, "Missing 'channels' parameter");
					return;
				}
				List<string> channels = JsonConvert.DeserializeObject<List<string>>(JsonConvert.SerializeObject(dictionary["channels"])) ?? new List<string>();
				List<string> subscribed = await _eventManager.SubscribeAsync(connection, channels);
				await SendResponseAsync(connection, request.Id, new { subscribed });
			}
			catch (Exception ex)
			{
				await SendErrorResponseAsync(connection, request.Id, -32603, "Subscription failed: " + ex.Message);
			}
		}

		private async Task HandleEventsUnsubscribeAsync(IConnection connection, GabpRequest request)
		{
			try
			{
				Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(request.Params));
				if (dictionary == null || !dictionary.ContainsKey("channels"))
				{
					await SendErrorResponseAsync(connection, request.Id, -32602, "Missing 'channels' parameter");
					return;
				}
				List<string> channels = JsonConvert.DeserializeObject<List<string>>(JsonConvert.SerializeObject(dictionary["channels"])) ?? new List<string>();
				List<string> unsubscribed = await _eventManager.UnsubscribeAsync(connection, channels);
				await SendResponseAsync(connection, request.Id, new { unsubscribed });
			}
			catch (Exception ex)
			{
				await SendErrorResponseAsync(connection, request.Id, -32603, "Unsubscription failed: " + ex.Message);
			}
		}

		private async Task HandleAttentionCurrentAsync(IConnection connection, GabpRequest request)
		{
			AttentionItem current = _attentionManager.GetCurrent();
			await SendResponseAsync(connection, request.Id, new AttentionCurrentResult
			{
				Attention = current
			});
		}

		private async Task HandleAttentionAckAsync(IConnection connection, GabpRequest request)
		{
			try
			{
				AttentionAckParams attentionAckParams = JsonConvert.DeserializeObject<AttentionAckParams>(JsonConvert.SerializeObject(request.Params));
				if (attentionAckParams == null || string.IsNullOrWhiteSpace(attentionAckParams.AttentionId))
				{
					await SendErrorResponseAsync(connection, request.Id, -32602, "Missing 'attentionId' parameter");
					return;
				}
				AttentionAckResult result = await _attentionManager.AcknowledgeAsync(attentionAckParams.AttentionId);
				await SendResponseAsync(connection, request.Id, result);
			}
			catch (Exception ex)
			{
				await SendErrorResponseAsync(connection, request.Id, -32603, "Attention acknowledgement failed: " + ex.Message);
			}
		}

		private async Task SendResponseAsync(IConnection connection, string requestId, object result)
		{
			GabpResponse message = new GabpResponse
			{
				Id = requestId,
				Result = result
			};
			await connection.SendMessageAsync(message);
		}

		private async Task SendErrorResponseAsync(IConnection connection, string requestId, int errorCode, string message, object data = null)
		{
			GabpResponse message2 = new GabpResponse
			{
				Id = requestId,
				Error = new GabpError
				{
					Code = errorCode,
					Message = message,
					Data = data
				}
			};
			await connection.SendMessageAsync(message2);
		}

		private void RegisterCoreTools()
		{
		}

		private void RegisterCoreEventChannels()
		{
			_eventManager.RegisterChannel("system/status", "System status events");
			_eventManager.RegisterChannel("system/log", "System log events");
			if (_attentionManager.IsEnabled)
			{
				_eventManager.RegisterChannel("attention/opened", "Attention lifecycle events when a blocking or advisory item opens");
				_eventManager.RegisterChannel("attention/updated", "Attention lifecycle events when the current item is updated");
				_eventManager.RegisterChannel("attention/cleared", "Attention lifecycle events when the current item is cleared");
			}
		}

		private List<string> GetSupportedProtocolMethods()
		{
			List<string> list = new List<string>(BaseProtocolMethods);
			if (_attentionManager.IsEnabled)
			{
				list.Add("attention/current");
				list.Add("attention/ack");
			}
			return list;
		}

		private void DisposeActiveSessions()
		{
			foreach (SessionInfo item in _sessions.Values.ToList())
			{
				try
				{
					item.Connection?.Dispose();
				}
				catch
				{
				}
			}
			_sessions.Clear();
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_transport.ConnectionEstablished -= OnConnectionEstablished;
				_transport.MessageReceived -= OnMessageReceived;
				try
				{
					_transport.StopAsync().GetAwaiter().GetResult();
				}
				catch
				{
				}
				DisposeActiveSessions();
				_transport?.Dispose();
			}
		}
	}
}
namespace Lib.GAB.Protocol
{
	public static class GabpErrorCodes
	{
		public const int InvalidRequest = -32600;

		public const int MethodNotFound = -32601;

		public const int InvalidParams = -32602;

		public const int InternalError = -32603;

		public const int ServerError = -32000;

		public const int AuthenticationFailed = -31000;

		public const int SessionNotEstablished = -31001;

		public const int ToolNotFound = -31002;

		public const int EventChannelNotFound = -31003;

		public const int ResourceNotFound = -31004;

		public const int MethodNotAllowed = -31005;
	}
	public abstract class GabpMessage
	{
		[JsonProperty("v")]
		public string Version { get; set; } = "gabp/1";

		[JsonProperty("id")]
		public string Id { get; set; } = Guid.NewGuid().ToString();

		[JsonProperty("type")]
		public abstract string Type { get; }
	}
	public class GabpRequest : GabpMessage
	{
		public override string Type => "request";

		[JsonProperty("method")]
		public string Method { get; set; } = string.Empty;

		[JsonProperty("params")]
		public object Params { get; set; }
	}
	public class GabpResponse : GabpMessage
	{
		public override string Type => "response";

		[JsonProperty("result")]
		public object Result { get; set; }

		[JsonProperty("error")]
		public GabpError Error { get; set; }
	}
	public class GabpEvent : GabpMessage
	{
		public override string Type => "event";

		[JsonProperty("channel")]
		public string Channel { get; set; } = string.Empty;

		[JsonProperty("seq")]
		public int Sequence { get; set; }

		[JsonProperty("payload")]
		public object Payload { get; set; }

		[JsonProperty("timestamp")]
		public DateTimeOffset? Timestamp { get; set; }
	}
	public class GabpError
	{
		[JsonProperty("code")]
		public int Code { get; set; }

		[JsonProperty("message")]
		public string Message { get; set; } = string.Empty;

		[JsonProperty("data")]
		public object Data { get; set; }
	}
	public class SessionHelloParams
	{
		[JsonProperty("token")]
		public string Token { get; set; } = string.Empty;

		[JsonProperty("bridgeVersion")]
		public string BridgeVersion { get; set; } = string.Empty;

		[JsonProperty("platform")]
		public string Platform { get; set; } = string.Empty;

		[JsonProperty("launchId")]
		public string LaunchId { get; set; } = string.Empty;
	}
	public class SessionWelcomeResult
	{
		[JsonProperty("agentId")]
		public string AgentId { get; set; } = string.Empty;

		[JsonProperty("app")]
		public AppInfo App { get; set; } = new AppInfo();

		[JsonProperty("capabilities")]
		public Capabilities Capabilities { get; set; } = new Capabilities();

		[JsonProperty("schemaVersion")]
		public string SchemaVersion { get; set; } = "1.0";
	}
	public class AppInfo
	{
		[JsonProperty("name")]
		public string Name { get; set; } = string.Empty;

		[JsonProperty("version")]
		public string Version { get; set; } = string.Empty;
	}
	public class Capabilities
	{
		[JsonProperty("methods")]
		public List<string> Methods { get; set; } = new List<string>();

		[JsonIgnore]
		public List<string> Tools
		{
			get
			{
				return Methods;
			}
			set
			{
				Methods = value ?? new List<string>();
			}
		}

		[JsonProperty("events")]
		public List<string> Events { get; set; } = new List<string>();

		[JsonProperty("resources")]
		public List<string> Resources { get; set; } = new List<string>();
	}
}
namespace Lib.GAB.Events
{
	public class EventManager : IEventManager
	{
		private class EventChannelState
		{
			public int SequenceNumber;

			public string Name { get; set; } = string.Empty;

			public string Description { get; set; }

			public HashSet<IConnection> Subscribers { get; set; } = new HashSet<IConnection>();
		}

		private readonly ConcurrentDictionary<string, EventChannelState> _channels = new ConcurrentDictionary<string, EventChannelState>();

		private readonly ConcurrentDictionary<string, HashSet<IConnection>> _subscriptions = new ConcurrentDictionary<string, HashSet<IConnection>>();

		private readonly object _lockObject = new object();

		public async Task<List<string>> SubscribeAsync(IConnection connection, List<string> channels)
		{
			List<string> list = new List<string>();
			lock (_lockObject)
			{
				foreach (string channel in channels)
				{
					if (_channels.ContainsKey(channel))
					{
						_channels[channel].Subscribers.Add(connection);
						if (!_subscriptions.ContainsKey(connection.Id))
						{
							_subscriptions[connection.Id] = new HashSet<IConnection>();
						}
						_subscriptions[connection.Id].Add(connection);
						list.Add(channel);
					}
				}
				connection.Disconnected += delegate
				{
					CleanupConnection(connection);
				};
			}
			return await Task.FromResult(list);
		}

		public async Task<List<string>> UnsubscribeAsync(IConnection connection, List<string> channels)
		{
			List<string> list = new List<string>();
			lock (_lockObject)
			{
				foreach (string channel in channels)
				{
					if (_channels.TryGetValue(channel, out var value) && value.Subscribers.Remove(connection))
					{
						list.Add(channel);
					}
				}
			}
			return await Task.FromResult(list);
		}

		public List<string> GetAvailableChannels()
		{
			return _channels.Keys.ToList();
		}

		public void RegisterChannel(string channel, string description = null)
		{
			if (string.IsNullOrWhiteSpace(channel))
			{
				throw new ArgumentException("Channel name cannot be null or empty", "channel");
			}
			_channels[channel] = new EventChannelState
			{
				Name = channel,
				Description = description,
				SequenceNumber = 0,
				Subscribers = new HashSet<IConnection>()
			};
		}

		public bool UnregisterChannel(string channel)
		{
			lock (_lockObject)
			{
				if (_channels.TryRemove(channel, out var value))
				{
					foreach (IConnection item in value.Subscribers.ToList())
					{
						if (_subscriptions.TryGetValue(item.Id, out var value2))
						{
							value2.Remove(item);
							if (value2.Count == 0)
							{
								_subscriptions.TryRemove(item.Id, out var _);
							}
						}
					}
					return true;
				}
				return false;
			}
		}

		public async Task EmitEventAsync(string channel, object payload, DateTimeOffset? timestamp = null)
		{
			if (!_channels.TryGetValue(channel, out var value))
			{
				return;
			}
			GabpEvent eventMessage = new GabpEvent
			{
				Channel = channel,
				Sequence = Interlocked.Increment(ref value.SequenceNumber),
				Payload = payload,
				Timestamp = (timestamp ?? DateTimeOffset.UtcNow)
			};
			await Task.WhenAll(((IEnumerable<IConnection>)value.Subscribers.ToList()).Select((Func<IConnection, Task>)async delegate(IConnection connection)
			{
				try
				{
					if (connection.IsConnected)
					{
						await connection.SendMessageAsync(eventMessage);
					}
					else
					{
						CleanupConnection(connection);
					}
				}
				catch
				{
					CleanupConnection(connection);
				}
			}));
		}

		public int GetSubscriberCount(string channel)
		{
			if (!_channels.TryGetValue(channel, out var value))
			{
				return 0;
			}
			return value.Subscribers.Count;
		}

		private void CleanupConnection(IConnection connection)
		{
			lock (_lockObject)
			{
				foreach (EventChannelState value2 in _channels.Values)
				{
					value2.Subscribers.Remove(connection);
				}
				_subscriptions.TryRemove(connection.Id, out var _);
			}
		}
	}
	public interface IEventManager
	{
		Task<List<string>> SubscribeAsync(IConnection connection, List<string> channels);

		Task<List<string>> UnsubscribeAsync(IConnection connection, List<string> channels);

		List<string> GetAvailableChannels();

		void RegisterChannel(string channel, string description = null);

		bool UnregisterChannel(string channel);

		Task EmitEventAsync(string channel, object payload, DateTimeOffset? timestamp = null);

		int GetSubscriberCount(string channel);
	}
	public class EventChannelInfo
	{
		public string Name { get; set; } = string.Empty;

		public string Description { get; set; }

		public int SubscriberCount { get; set; }
	}
}
namespace Lib.GAB.Attention
{
	public static class AttentionProtocol
	{
		public const string CurrentMethod = "attention/current";

		public const string AckMethod = "attention/ack";

		public const string OpenedChannel = "attention/opened";

		public const string UpdatedChannel = "attention/updated";

		public const string ClearedChannel = "attention/cleared";
	}
	public class AttentionSample
	{
		[JsonProperty("level")]
		public string Level { get; set; } = string.Empty;

		[JsonProperty("message")]
		public string Message { get; set; } = string.Empty;

		[JsonProperty("repeatCount")]
		public int RepeatCount { get; set; }

		[JsonProperty("latestSequence")]
		public long LatestSequence { get; set; }

		public AttentionSample Clone()
		{
			return new AttentionSample
			{
				Level = Level,
				Message = Message,
				RepeatCount = RepeatCount,
				LatestSequence = LatestSequence
			};
		}
	}
	public class AttentionItem
	{
		[JsonProperty("attentionId")]
		public string AttentionId { get; set; } = string.Empty;

		[JsonProperty("state")]
		public string State { get; set; } = "open";

		[JsonProperty("severity")]
		public string Severity { get; set; } = "error";

		[JsonProperty("blocking")]
		public bool Blocking { get; set; }

		[JsonProperty("stateInvalidated")]
		public bool StateInvalidated { get; set; }

		[JsonProperty("summary")]
		public string Summary { get; set; } = string.Empty;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string CausalOperationId { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string CausalMethod { get; set; }

		[JsonProperty("openedAtSequence")]
		public long OpenedAtSequence { get; set; }

		[JsonProperty("latestSequence")]
		public long LatestSequence { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public long? DiagnosticsCursor { get; set; }

		[JsonProperty("totalUrgentEntries")]
		public int TotalUrgentEntries { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<AttentionSample> Sample { get; set; } = new List<AttentionSample>();

		public AttentionItem Clone()
		{
			return new AttentionItem
			{
				AttentionId = AttentionId,
				State = State,
				Severity = Severity,
				Blocking = Blocking,
				StateInvalidated = StateInvalidated,
				Summary = Summary,
				CausalOperationId = CausalOperationId,
				CausalMethod = CausalMethod,
				OpenedAtSequence = OpenedAtSequence,
				LatestSequence = LatestSequence,
				DiagnosticsCursor = DiagnosticsCursor,
				TotalUrgentEntries = TotalUrgentEntries,
				Sample = ((Sample == null) ? new List<AttentionSample>() : Sample.Select((AttentionSample entry) => entry.Clone()).ToList())
			};
		}
	}
	public class AttentionCurrentResult
	{
		[JsonProperty("attention")]
		public AttentionItem Attention { get; set; }
	}
	public class AttentionAckParams
	{
		[JsonProperty("attentionId")]
		public string AttentionId { get; set; } = string.Empty;
	}
	public class AttentionAckResult
	{
		[JsonProperty("acknowledged")]
		public bool Acknowledged { get; set; }

		[JsonProperty("attentionId")]
		public string AttentionId { get; set; }

		[JsonProperty("currentAttention")]
		public AttentionItem CurrentAttention { get; set; }
	}
	public class AttentionManager : IAttentionManager
	{
		private readonly IEventManager _eventManager;

		private readonly bool _isEnabled;

		private readonly object _lockObject = new object();

		private AttentionItem _currentAttention;

		public bool IsEnabled => _isEnabled;

		public AttentionManager(IEventManager eventManager, bool isEnabled)
		{
			_eventManager = eventManager ?? throw new ArgumentNullException("eventManager");
			_isEnabled = isEnabled;
		}

		public AttentionItem GetCurrent()
		{
			lock (_lockObject)
			{
				return _currentAttention?.Clone();
			}
		}

		public async Task<AttentionItem> PublishAsync(AttentionItem attention, DateTimeOffset? timestamp = null)
		{
			EnsureEnabled();
			if (attention == null)
			{
				throw new ArgumentNullException("attention");
			}
			if (string.IsNullOrWhiteSpace(attention.AttentionId))
			{
				throw new ArgumentException("Attention item must have a non-empty attentionId.", "attention");
			}
			AttentionItem normalized;
			string channel;
			lock (_lockObject)
			{
				AttentionItem currentAttention = _currentAttention;
				normalized = NormalizePublishedAttention(attention, currentAttention);
				channel = ((currentAttention != null && string.Equals(currentAttention.AttentionId, normalized.AttentionId, StringComparison.Ordinal)) ? "attention/updated" : "attention/opened");
				_currentAttention = normalized.Clone();
			}
			await _eventManager.EmitEventAsync(channel, normalized, timestamp);
			return normalized.Clone();
		}

		public async Task<AttentionItem> ClearAsync(AttentionItem clearedAttention = null, DateTimeOffset? timestamp = null)
		{
			EnsureEnabled();
			AttentionItem cleared;
			lock (_lockObject)
			{
				if (_currentAttention == null)
				{
					return null;
				}
				cleared = NormalizeClearedAttention(clearedAttention, _currentAttention);
				_currentAttention = null;
			}
			await _eventManager.EmitEventAsync("attention/cleared", cleared, timestamp);
			return cleared.Clone();
		}

		public async Task<AttentionAckResult> AcknowledgeAsync(string attentionId, DateTimeOffset? timestamp = null)
		{
			EnsureEnabled();
			if (string.IsNullOrWhiteSpace(attentionId))
			{
				throw new ArgumentException("Attention acknowledgement requires a non-empty attentionId.", "attentionId");
			}
			AttentionItem attentionItem = null;
			AttentionItem currentAttention = null;
			bool acknowledged = false;
			lock (_lockObject)
			{
				if (_currentAttention != null && string.Equals(_currentAttention.AttentionId, attentionId, StringComparison.Ordinal))
				{
					attentionItem = NormalizeClearedAttention(null, _currentAttention);
					_currentAttention = null;
					acknowledged = true;
				}
				else if (_currentAttention != null)
				{
					currentAttention = _currentAttention.Clone();
				}
			}
			if (attentionItem != null)
			{
				await _eventManager.EmitEventAsync("attention/cleared", attentionItem, timestamp);
			}
			return new AttentionAckResult
			{
				Acknowledged = acknowledged,
				AttentionId = attentionId,
				CurrentAttention = currentAttention?.Clone()
			};
		}

		private void EnsureEnabled()
		{
			if (!_isEnabled)
			{
				throw new InvalidOperationException("Attention support is not enabled for this server.");
			}
		}

		private static AttentionItem NormalizePublishedAttention(AttentionItem incoming, AttentionItem current)
		{
			AttentionItem attentionItem = incoming.Clone();
			attentionItem.State = "open";
			if (current != null && string.Equals(current.AttentionId, attentionItem.AttentionId, StringComparison.Ordinal))
			{
				if (attentionItem.OpenedAtSequence == 0L)
				{
					attentionItem.OpenedAtSequence = current.OpenedAtSequence;
				}
				if (attentionItem.LatestSequence == 0L)
				{
					attentionItem.LatestSequence = current.LatestSequence;
				}
				if (attentionItem.TotalUrgentEntries == 0)
				{
					attentionItem.TotalUrgentEntries = current.TotalUrgentEntries;
				}
			}
			else if (attentionItem.LatestSequence == 0L)
			{
				attentionItem.LatestSequence = attentionItem.OpenedAtSequence;
			}
			return attentionItem;
		}

		private static AttentionItem NormalizeClearedAttention(AttentionItem clearedAttention, AttentionItem current)
		{
			AttentionItem attentionItem = (clearedAttention ?? current).Clone();
			attentionItem.AttentionId = (string.IsNullOrWhiteSpace(attentionItem.AttentionId) ? current.AttentionId : attentionItem.AttentionId);
			attentionItem.State = "cleared";
			attentionItem.Blocking = false;
			if (attentionItem.OpenedAtSequence == 0L)
			{
				attentionItem.OpenedAtSequence = current.OpenedAtSequence;
			}
			if (attentionItem.LatestSequence == 0L)
			{
				attentionItem.LatestSequence = current.LatestSequence;
			}
			if (attentionItem.TotalUrgentEntries == 0)
			{
				attentionItem.TotalUrgentEntries = current.TotalUrgentEntries;
			}
			if (string.IsNullOrWhiteSpace(attentionItem.Severity))
			{
				attentionItem.Severity = current.Severity;
			}
			if (string.IsNullOrWhiteSpace(attentionItem.Summary))
			{
				attentionItem.Summary = current.Summary;
			}
			if (string.IsNullOrWhiteSpace(attentionItem.CausalOperationId))
			{
				attentionItem.CausalOperationId = current.CausalOperationId;
			}
			if (string.IsNullOrWhiteSpace(attentionItem.CausalMethod))
			{
				attentionItem.CausalMethod = current.CausalMethod;
			}
			if (!attentionItem.DiagnosticsCursor.HasValue)
			{
				attentionItem.DiagnosticsCursor = current.DiagnosticsCursor;
			}
			if ((attentionItem.Sample == null || attentionItem.Sample.Count == 0) && current.Sample != null)
			{
				attentionItem.Sample = current.Sample;
			}
			return attentionItem;
		}
	}
	public interface IAttentionManager
	{
		bool IsEnabled { get; }

		AttentionItem GetCurrent();

		Task<AttentionItem> PublishAsync(AttentionItem attention, DateTimeOffset? timestamp = null);

		Task<AttentionItem> ClearAsync(AttentionItem clearedAttention = null, DateTimeOffset? timestamp = null);

		Task<AttentionAckResult> AcknowledgeAsync(string attentionId, DateTimeOffset? timestamp = null);
	}
}

plugins/Gabp.Runtime.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("GABP Contributors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Shared runtime primitives for the Game Agent Bridge Protocol (GABP).")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ce258707e15bc45e4303e9785ce49bc1fe6506cf")]
[assembly: AssemblyProduct("Gabp.Runtime")]
[assembly: AssemblyTitle("Gabp.Runtime")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/pardeike/gabp-runtime")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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 Gabp.Runtime
{
	public class GabpAttentionSample
	{
		[JsonProperty("level")]
		public string Level { get; set; } = string.Empty;

		[JsonProperty("message")]
		public string Message { get; set; } = string.Empty;

		[JsonProperty("repeatCount")]
		public int RepeatCount { get; set; }

		[JsonProperty("latestSequence")]
		public int LatestSequence { get; set; }
	}
	public class GabpAttention
	{
		[JsonProperty("attentionId")]
		public string AttentionId { get; set; } = string.Empty;

		[JsonProperty("state")]
		public string State { get; set; } = string.Empty;

		[JsonProperty("severity")]
		public string Severity { get; set; } = string.Empty;

		[JsonProperty("blocking")]
		public bool Blocking { get; set; }

		[JsonProperty("stateInvalidated")]
		public bool StateInvalidated { get; set; }

		[JsonProperty("summary")]
		public string Summary { get; set; } = string.Empty;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? CausalOperationId { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? CausalMethod { get; set; }

		[JsonProperty("openedAtSequence")]
		public int OpenedAtSequence { get; set; }

		[JsonProperty("latestSequence")]
		public int LatestSequence { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public int? DiagnosticsCursor { get; set; }

		[JsonProperty("totalUrgentEntries")]
		public int TotalUrgentEntries { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<GabpAttentionSample>? Sample { get; set; }
	}
	public class AttentionCurrentParams
	{
	}
	public class AttentionCurrentRequest : GabpRequestEnvelope<AttentionCurrentParams>
	{
		public AttentionCurrentRequest()
		{
			base.Method = "attention/current";
			base.Params = new AttentionCurrentParams();
		}
	}
	public class AttentionCurrentResult
	{
		[JsonProperty("attention")]
		public GabpAttention? Attention { get; set; }
	}
	public class AttentionCurrentResponse : GabpResponseEnvelope<AttentionCurrentResult>
	{
		public AttentionCurrentResponse()
		{
			base.Result = new AttentionCurrentResult();
		}
	}
	public class AttentionAckParams
	{
		[JsonProperty("attentionId")]
		public string AttentionId { get; set; } = string.Empty;
	}
	public class AttentionAckRequest : GabpRequestEnvelope<AttentionAckParams>
	{
		public AttentionAckRequest()
		{
			base.Method = "attention/ack";
			base.Params = new AttentionAckParams();
		}
	}
	public class AttentionAckResult
	{
		[JsonProperty("acknowledged")]
		public bool Acknowledged { get; set; }

		[JsonProperty("attentionId")]
		public string AttentionId { get; set; } = string.Empty;

		[JsonProperty("currentAttention")]
		public GabpAttention? CurrentAttention { get; set; }
	}
	public class AttentionAckResponse : GabpResponseEnvelope<AttentionAckResult>
	{
		public AttentionAckResponse()
		{
			base.Result = new AttentionAckResult();
		}
	}
	public class GabpCapabilities
	{
		[JsonProperty("methods")]
		public List<string> Methods { get; set; } = new List<string>();

		[JsonProperty("events")]
		public List<string> Events { get; set; } = new List<string>();

		[JsonProperty("resources")]
		public List<string> Resources { get; set; } = new List<string>();

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public Dictionary<string, object>? Extensions { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public GabpLimits? Limits { get; set; }
	}
	public class GabpLimits
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public int? MaxMessageSize { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public int? MaxConcurrentRequests { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public int? RequestTimeout { get; set; }
	}
	public class GabpError
	{
		[JsonProperty("code")]
		public int Code { get; set; }

		[JsonProperty("message")]
		public string Message { get; set; } = string.Empty;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public JToken? Data { get; set; }
	}
	public static class GabpJson
	{
		private static readonly JsonSerializerSettings DefaultSettings = CreateDefaultSettings();

		public static string Serialize<T>(T value)
		{
			return JsonConvert.SerializeObject((object)value, DefaultSettings);
		}

		public static byte[] SerializeToUtf8Bytes<T>(T value)
		{
			return Encoding.UTF8.GetBytes(Serialize(value));
		}

		public static T Deserialize<T>(string json)
		{
			return JsonConvert.DeserializeObject<T>(json, DefaultSettings);
		}

		public static T Deserialize<T>(byte[] json)
		{
			return Deserialize<T>(Encoding.UTF8.GetString(json));
		}

		private static JsonSerializerSettings CreateDefaultSettings()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			return new JsonSerializerSettings
			{
				NullValueHandling = (NullValueHandling)1
			};
		}
	}
	public static class GabpProtocol
	{
		public const string EnvelopeVersion = "gabp/1";

		public const string RequestType = "request";

		public const string ResponseType = "response";

		public const string EventType = "event";

		public const string SessionHelloMethod = "session/hello";

		public const string ToolsCallMethod = "tools/call";

		public const string ToolsListMethod = "tools/list";

		public const string AttentionCurrentMethod = "attention/current";

		public const string AttentionAckMethod = "attention/ack";

		public const string AttentionOpenedChannel = "attention/opened";

		public const string AttentionUpdatedChannel = "attention/updated";

		public const string AttentionClearedChannel = "attention/cleared";
	}
	public class GabpRequestEnvelope<TParams>
	{
		[JsonProperty("v")]
		public string Version { get; set; } = "gabp/1";

		[JsonProperty("id")]
		public string Id { get; set; } = string.Empty;

		[JsonProperty("type")]
		public string Type { get; set; } = "request";

		[JsonProperty("method")]
		public string Method { get; set; } = string.Empty;

		[JsonProperty("params")]
		public TParams Params { get; set; }
	}
	public class GabpResponseEnvelope<TResult>
	{
		[JsonProperty("v")]
		public string Version { get; set; } = "gabp/1";

		[JsonProperty("id")]
		public string Id { get; set; } = string.Empty;

		[JsonProperty("type")]
		public string Type { get; set; } = "response";

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public TResult? Result { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public GabpError? Error { get; set; }
	}
	public class GabpEventEnvelope<TPayload>
	{
		[JsonProperty("v")]
		public string Version { get; set; } = "gabp/1";

		[JsonProperty("id")]
		public string Id { get; set; } = string.Empty;

		[JsonProperty("type")]
		public string Type { get; set; } = "event";

		[JsonProperty("channel")]
		public string Channel { get; set; } = string.Empty;

		[JsonProperty("seq")]
		public int Sequence { get; set; }

		[JsonProperty("payload")]
		public TPayload Payload { get; set; }
	}
	public static class RuntimeMetadata
	{
		public const string PackageId = "Gabp.Runtime";

		public const string Version = "1.0.0";

		public const string DevelopmentVersion = "1.0.0";

		public const string TargetGabpSchemaVersion = "1.0";
	}
	public class GabpClientInfo
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Name { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Version { get; set; }
	}
	public class GabpAppInfo
	{
		[JsonProperty("name")]
		public string Name { get; set; } = string.Empty;

		[JsonProperty("version")]
		public string Version { get; set; } = string.Empty;
	}
	public class GabpServerInfo
	{
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Name { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Version { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Author { get; set; }
	}
	public class SessionHelloParams
	{
		[JsonProperty("token")]
		public string Token { get; set; } = string.Empty;

		[JsonProperty("bridgeVersion")]
		public string BridgeVersion { get; set; } = string.Empty;

		[JsonProperty("platform")]
		public string Platform { get; set; } = string.Empty;

		[JsonProperty("launchId")]
		public string LaunchId { get; set; } = string.Empty;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public GabpClientInfo? ClientInfo { get; set; }
	}
	public class SessionHelloRequest : GabpRequestEnvelope<SessionHelloParams>
	{
		public SessionHelloRequest()
		{
			base.Method = "session/hello";
			base.Params = new SessionHelloParams();
		}
	}
	public class SessionWelcomeResult
	{
		[JsonProperty("agentId")]
		public string AgentId { get; set; } = string.Empty;

		[JsonProperty("app")]
		public GabpAppInfo App { get; set; } = new GabpAppInfo();

		[JsonProperty("capabilities")]
		public GabpCapabilities Capabilities { get; set; } = new GabpCapabilities();

		[JsonProperty("schemaVersion")]
		public string SchemaVersion { get; set; } = "1.0";

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public GabpServerInfo? ServerInfo { get; set; }
	}
	public class SessionWelcomeResponse : GabpResponseEnvelope<SessionWelcomeResult>
	{
		public SessionWelcomeResponse()
		{
			base.Result = new SessionWelcomeResult();
		}
	}
	public class GabpToolDescriptor
	{
		[JsonProperty("name")]
		public string Name { get; set; } = string.Empty;

		[JsonProperty("title")]
		public string Title { get; set; } = string.Empty;

		[JsonProperty("description")]
		public string Description { get; set; } = string.Empty;

		[JsonProperty("inputSchema")]
		public JToken InputSchema { get; set; } = (JToken)new JObject();

		[JsonProperty("outputSchema")]
		public JToken OutputSchema { get; set; } = (JToken)new JObject();

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public List<string>? Tags { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public bool? Deprecated { get; set; }

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string? Version { get; set; }
	}
	public class ToolsCallParams
	{
		[JsonProperty("name")]
		public string Name { get; set; } = string.Empty;

		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public JToken? Arguments { get; set; }
	}
	public class ToolsCallRequest : GabpRequestEnvelope<ToolsCallParams>
	{
		public ToolsCallRequest()
		{
			base.Method = "tools/call";
			base.Params = new ToolsCallParams();
		}
	}
	public class ToolsListResult
	{
		[JsonProperty("tools")]
		public List<GabpToolDescriptor> Tools { get; set; } = new List<GabpToolDescriptor>();
	}
	public class ToolsListResponse : GabpResponseEnvelope<ToolsListResult>
	{
		public ToolsListResponse()
		{
			base.Result = new ToolsListResult();
		}
	}
}