Decompiled source of SyncUpgrades v1.9.2

SyncUpgrades.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using SyncUpgrades.Core;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SyncUpgrades")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.9.2.0")]
[assembly: AssemblyInformationalVersion("1.9.2+4d8d088cca59d5905472fe1370f53a5054944036")]
[assembly: AssemblyProduct("SyncUpgrades")]
[assembly: AssemblyTitle("SyncUpgrades")]
[assembly: AssemblyVersion("1.9.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SyncUpgrades
{
	[BepInPlugin("TGO.SyncUpgrades", "Sync Upgrades", "1.9.2")]
	[BepInDependency("REPOLib", "2.1.0")]
	public class Entry : BaseUnityPlugin
	{
		private const string PluginName = "Sync Upgrades";

		private const string PluginVersion = "1.9.2";

		private const string PluginId = "TGO.SyncUpgrades";

		private static readonly Harmony Harmony = new Harmony("TGO.SyncUpgrades");

		internal static readonly ManualLogSource LogSource = Logger.CreateLogSource("Sync Upgrades");

		private static Entry? _instance;

		internal static ConfigFile BepConfig => ((BaseUnityPlugin)_instance).Config;

		private void Awake()
		{
			_instance = this;
			SyncManager.Init();
			Harmony.PatchAll();
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)4;
			LogSource.LogInfo((object)"Sync Upgrades loaded!");
		}
	}
}
namespace SyncUpgrades.Patches
{
	[HarmonyPatch(typeof(PunManager))]
	public class PunManagerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("UpdateHealthRightAway", new Type[] { typeof(string) })]
		private static void UpdateHealthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string playerName)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, playerName), SyncUtil.HealthId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateEnergyRightAway", new Type[] { typeof(string) })]
		private static void UpdateEnergyRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.StaminaId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateTumbleLaunchRightAway", new Type[] { typeof(string) })]
		private static void UpdateTumbleLaunchRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.TumbleLaunchId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateSprintSpeedRightAway", new Type[] { typeof(string) })]
		private static void UpdateSprintSpeedRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.SprintSpeedId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateGrabStrengthRightAway", new Type[] { typeof(string) })]
		private static void UpdateGrabStrengthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.GrabStrengthId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateThrowStrengthRightAway", new Type[] { typeof(string) })]
		private static void UpdateThrowStrengthRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.GrabThrowId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateGrabRangeRightAway", new Type[] { typeof(string) })]
		private static void UpdateGrabRangeRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.GrabRangeId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateExtraJumpRightAway", new Type[] { typeof(string) })]
		private static void UpdateExtraJumpRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.ExtraJumpId);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateMapPlayerCountRightAway", new Type[] { typeof(string) })]
		private static void UpdateMapPlayerCountRightAway(PunManager __instance, PhotonView ___photonView, StatsManager ___statsManager, string _steamID)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.PlayerUpgradeStat(new PunBundle(__instance, ___photonView, ___statsManager, _steamID), SyncUtil.MapPlayerCountId);
			}
		}
	}
	[HarmonyPatch(typeof(RunManager))]
	internal class RunManagerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("ChangeLevel")]
		private static void ChangeLevel(bool _completedLevel, bool _levelFailed, bool ___restarting)
		{
			if (!(SemiFunc.IsNotMasterClient() || _levelFailed))
			{
				SyncManager.SyncAll(new PunBundle(PunManager.instance, PunManager.instance.GetView(), StatsManager.instance, SyncUtil.HostSteamId));
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager))]
	internal class StatsManagerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("PlayerAdd", new Type[]
		{
			typeof(string),
			typeof(string)
		})]
		private static void PlayerAdd(string _steamID, string _playerName)
		{
			if (!SemiFunc.IsNotMasterClient())
			{
				SyncManager.SyncUpgrades(_steamID);
			}
		}
	}
}
namespace SyncUpgrades.Core
{
	public static class Extensions
	{
		public static string SteamId(this PlayerAvatar avatar)
		{
			return SemiFunc.PlayerGetSteamID(avatar);
		}

		public static PhotonView GetView(this PunManager instance)
		{
			return ((Component)instance).GetComponent<PhotonView>();
		}
	}
	public class PunBundle
	{
		public PunManager Manager { get; }

		public PhotonView View { get; }

		public StatsManager Stats { get; }

		public string SteamId { get; }

		public PunBundle(PunManager mgr, PhotonView pv, StatsManager sts, string sId)
		{
			Manager = mgr;
			View = pv;
			Stats = sts;
			SteamId = sId;
			base..ctor();
		}

		public PunBundle(PunManager mgr, StatsManager sts, string sId)
			: this(mgr, mgr.GetView(), sts, sId)
		{
		}
	}
	[PublicAPI]
	public static class SyncUtil
	{
		private const RpcTarget Others = 1;

		public static readonly UpgradeId HealthId = new UpgradeId(UpgradeType.Health);

		public static readonly UpgradeId StaminaId = new UpgradeId(UpgradeType.Stamina);

		public static readonly UpgradeId ExtraJumpId = new UpgradeId(UpgradeType.ExtraJump);

		public static readonly UpgradeId TumbleLaunchId = new UpgradeId(UpgradeType.TumbleLaunch);

		public static readonly UpgradeId MapPlayerCountId = new UpgradeId(UpgradeType.MapPlayerCount);

		public static readonly UpgradeId SprintSpeedId = new UpgradeId(UpgradeType.SprintSpeed);

		public static readonly UpgradeId GrabStrengthId = new UpgradeId(UpgradeType.GrabStrength);

		public static readonly UpgradeId GrabRangeId = new UpgradeId(UpgradeType.GrabRange);

		public static readonly UpgradeId GrabThrowId = new UpgradeId(UpgradeType.GrabThrow);

		public static string HostSteamId => Local.SteamId();

		public static PlayerAvatar Local => SemiFunc.PlayerAvatarLocal();

		public static string TrimKey(string? key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return string.Empty;
			}
			string text;
			if (key.StartsWith("appliedPlayerUpgrade"))
			{
				text = key;
				return text.Substring(20, text.Length - 20);
			}
			if (!key.StartsWith("playerUpgrade"))
			{
				return key;
			}
			text = key;
			return text.Substring(13, text.Length - 13);
		}

		public static UpgradeType GetUpgradeType(string? key)
		{
			return TrimKey(key) switch
			{
				"Health" => UpgradeType.Health, 
				"Stamina" => UpgradeType.Stamina, 
				"ExtraJump" => UpgradeType.ExtraJump, 
				"Launch" => UpgradeType.TumbleLaunch, 
				"MapPlayerCount" => UpgradeType.MapPlayerCount, 
				"Speed" => UpgradeType.SprintSpeed, 
				"Strength" => UpgradeType.GrabStrength, 
				"Range" => UpgradeType.GrabRange, 
				"Throw" => UpgradeType.GrabThrow, 
				_ => UpgradeType.Modded, 
			};
		}

		public static string GetUpgradeName(UpgradeType key)
		{
			return key switch
			{
				UpgradeType.Health => "Health", 
				UpgradeType.Stamina => "Stamina", 
				UpgradeType.ExtraJump => "ExtraJump", 
				UpgradeType.TumbleLaunch => "Launch", 
				UpgradeType.MapPlayerCount => "MapPlayerCount", 
				UpgradeType.SprintSpeed => "Speed", 
				UpgradeType.GrabStrength => "Strength", 
				UpgradeType.GrabRange => "Range", 
				UpgradeType.GrabThrow => "Throw", 
				_ => throw new ArgumentException(), 
			};
		}

		public static Dictionary<string, int> GetUpgrades(StatsManager stats, UpgradeId id)
		{
			return id.Type switch
			{
				UpgradeType.Health => stats.playerUpgradeHealth, 
				UpgradeType.Stamina => stats.playerUpgradeStamina, 
				UpgradeType.ExtraJump => stats.playerUpgradeExtraJump, 
				UpgradeType.TumbleLaunch => stats.playerUpgradeLaunch, 
				UpgradeType.MapPlayerCount => stats.playerUpgradeMapPlayerCount, 
				UpgradeType.SprintSpeed => stats.playerUpgradeSpeed, 
				UpgradeType.GrabStrength => stats.playerUpgradeStrength, 
				UpgradeType.GrabRange => stats.playerUpgradeRange, 
				UpgradeType.GrabThrow => stats.playerUpgradeThrow, 
				UpgradeType.Modded => stats.dictionaryOfDictionaries[id.RawName], 
				_ => throw new ArgumentException(), 
			};
		}

		public static string GetRPCFunctionName(UpgradeType key)
		{
			return key switch
			{
				UpgradeType.Health => "UpgradePlayerHealthRPC", 
				UpgradeType.Stamina => "UpgradePlayerEnergyRPC", 
				UpgradeType.ExtraJump => "UpgradePlayerExtraJumpRPC", 
				UpgradeType.TumbleLaunch => "UpgradePlayerTumbleLaunchRPC", 
				UpgradeType.MapPlayerCount => "UpgradeMapPlayerCountRPC", 
				UpgradeType.SprintSpeed => "UpgradePlayerSprintSpeedRPC", 
				UpgradeType.GrabStrength => "UpgradePlayerGrabStrengthRPC", 
				UpgradeType.GrabRange => "UpgradePlayerGrabRangeRPC", 
				UpgradeType.GrabThrow => "UpgradePlayerThrowStrengthRPC", 
				_ => throw new ArgumentException(), 
			};
		}

		public static void CallRPCOnePlayer(PunBundle bundle, PlayerAvatar workingPlayer, UpgradeId key)
		{
			CallRPCOnePlayer(bundle, workingPlayer.SteamId(), key, workingPlayer.photonView.Owner);
		}

		public static void CallRPCOnePlayer(PunBundle bundle, string steamId, UpgradeId key, Player player)
		{
			bundle.View.RPC(GetRPCFunctionName(key.Type), player, new object[2]
			{
				steamId,
				++GetUpgrades(bundle.Stats, key)[steamId]
			});
		}

		public static void SyncStatsDictionaryToAll(PunBundle bundle)
		{
			bundle.Manager.SyncAllDictionaries();
		}

		public static void CallRPC(PunBundle bundle, string steamId, UpgradeId key)
		{
			bundle.View.RPC(GetRPCFunctionName(key.Type), (RpcTarget)1, new object[2]
			{
				steamId,
				++GetUpgrades(bundle.Stats, key)[steamId]
			});
		}

		public static int CallUpdateFunction(PunManager instance, string steamId, UpgradeType key)
		{
			return key switch
			{
				UpgradeType.Health => instance.UpgradePlayerHealth(steamId), 
				UpgradeType.Stamina => instance.UpgradePlayerEnergy(steamId), 
				UpgradeType.ExtraJump => instance.UpgradePlayerExtraJump(steamId), 
				UpgradeType.TumbleLaunch => instance.UpgradePlayerTumbleLaunch(steamId), 
				UpgradeType.MapPlayerCount => instance.UpgradeMapPlayerCount(steamId), 
				UpgradeType.SprintSpeed => instance.UpgradePlayerSprintSpeed(steamId), 
				UpgradeType.GrabStrength => instance.UpgradePlayerGrabStrength(steamId), 
				UpgradeType.GrabRange => instance.UpgradePlayerGrabRange(steamId), 
				UpgradeType.GrabThrow => instance.UpgradePlayerThrowStrength(steamId), 
				_ => throw new ArgumentException(), 
			};
		}

		public static void UpgradeModded(PunBundle bundle, PlayerAvatar workingPlayer, UpgradeId key, int amount)
		{
			string text = workingPlayer.SteamId();
			int num = (bundle.Stats.dictionaryOfDictionaries[key.RawName][text] += amount);
			PlayerUpgrade val = default(PlayerUpgrade);
			if (Upgrades.TryGetUpgrade(TrimKey(key.RawName), ref val))
			{
				val.SetLevel(workingPlayer, num);
			}
			else
			{
				IncrementUpdateDict(bundle, text, key, amount);
			}
		}

		public static void IncrementUpdateDictAndSync(PunBundle bundle, string steamId, UpgradeId key, int amount)
		{
			bundle.Stats.dictionaryOfDictionaries[key.RawName][steamId] += amount;
			bundle.Manager.SyncAllDictionaries();
		}

		public static void IncrementUpdateDict(PunBundle bundle, string steamId, UpgradeId key, int amount)
		{
			bundle.Stats.dictionaryOfDictionaries[key.RawName][steamId] += amount;
		}
	}
	[PublicAPI]
	public static class SyncManager
	{
		private static ConfigEntry<bool>? _syncHealth;

		private static ConfigEntry<bool>? _syncStamina;

		private static ConfigEntry<bool>? _syncExtraJump;

		private static ConfigEntry<bool>? _syncMapPlayerCount;

		private static ConfigEntry<bool>? _syncGrabRange;

		private static ConfigEntry<bool>? _syncGrabStrength;

		private static ConfigEntry<bool>? _syncGrabThrow;

		private static ConfigEntry<bool>? _syncSprintSpeed;

		private static ConfigEntry<bool>? _syncTumbleLaunch;

		private static ConfigEntry<bool>? _moddedUpgrades;

		internal static void Init()
		{
			_syncHealth = Entry.BepConfig.Bind<bool>("Sync", "Health", true, "Sync Max Health");
			_syncStamina = Entry.BepConfig.Bind<bool>("Sync", "Stamina", true, "Sync Max Stamina");
			_syncExtraJump = Entry.BepConfig.Bind<bool>("Sync", "Extra Jump", true, "Sync Extra Jump Count");
			_syncTumbleLaunch = Entry.BepConfig.Bind<bool>("Sync", "Tumble Launch", true, "Sync Tumble Launch Count");
			_syncMapPlayerCount = Entry.BepConfig.Bind<bool>("Sync", "Map Player Count", true, "Sync Map Player Count");
			_syncSprintSpeed = Entry.BepConfig.Bind<bool>("Sync", "Sprint Speed", false, "Sync Sprint Speed");
			_syncGrabStrength = Entry.BepConfig.Bind<bool>("Sync", "Grab Strength", true, "Sync Grab Strength");
			_syncGrabRange = Entry.BepConfig.Bind<bool>("Sync", "Grab Range", true, "Sync Grab Range");
			_syncGrabThrow = Entry.BepConfig.Bind<bool>("Sync", "Grab Throw", true, "Sync Grab Throw");
			_moddedUpgrades = Entry.BepConfig.Bind<bool>("Sync", "Modded Upgrades", true, "Sync Misc Modded Upgrades");
		}

		public static void SyncUpgrades(string steamID)
		{
			SyncUpgrades(new PunBundle(PunManager.instance, StatsManager.instance, steamID), SemiFunc.PlayerAvatarGetFromSteamID(steamID), dict: true);
		}

		private static IEnumerable<UpgradeId> GetUpgradeTypes(PunBundle bundle)
		{
			return from kvp in bundle.Stats.dictionaryOfDictionaries
				where kvp.Key.StartsWith("playerUpgrade") || kvp.Key.StartsWith("appliedPlayerUpgrade")
				select new UpgradeId(kvp.Key);
		}

		private static void SyncUpgrades(PunBundle bundle, PlayerAvatar workingPlayer, bool dict = false)
		{
			string hostSteamId = SyncUtil.HostSteamId;
			string text = workingPlayer.SteamId();
			if (text == SyncUtil.HostSteamId)
			{
				return;
			}
			foreach (UpgradeId item in GetUpgradeTypes(bundle).Where(ShouldSync))
			{
				Dictionary<string, int> upgrades = SyncUtil.GetUpgrades(bundle.Stats, item);
				int valueOrDefault = upgrades.GetValueOrDefault(text, 0);
				if (!upgrades.TryGetValue(hostSteamId, out var value) || value <= valueOrDefault)
				{
					continue;
				}
				int num = value - valueOrDefault;
				if (item.Type != 0)
				{
					for (int i = 0; i < num; i++)
					{
						SyncUtil.CallRPCOnePlayer(bundle, workingPlayer, item);
					}
				}
				else
				{
					SyncUtil.UpgradeModded(bundle, workingPlayer, item, num);
				}
				Entry.LogSource.LogInfo((object)$"Synchronized upgrade for player {text}: {item.RawName} ({item.Type}), from {valueOrDefault} to {value}");
			}
			if (dict)
			{
				SyncUtil.SyncStatsDictionaryToAll(bundle);
			}
		}

		public static void PlayerUpgradeStat(PunBundle bundle, UpgradeId upgradeId)
		{
			if (ShouldSync(upgradeId))
			{
				if (bundle.SteamId != SyncUtil.HostSteamId)
				{
					SyncUtil.CallUpdateFunction(bundle.Manager, SyncUtil.HostSteamId, upgradeId.Type);
				}
				else
				{
					SyncAll(bundle);
				}
			}
		}

		public static void SyncAll(PunBundle bundle)
		{
			foreach (PlayerAvatar item in from avatar in SemiFunc.PlayerGetAll()
				where avatar.SteamId() != SyncUtil.HostSteamId
				select avatar)
			{
				SyncUpgrades(bundle, item);
			}
			SyncUtil.SyncStatsDictionaryToAll(bundle);
		}

		private static bool ShouldSync(UpgradeId key)
		{
			return key.Type switch
			{
				UpgradeType.Health => _syncHealth?.Value ?? false, 
				UpgradeType.Stamina => _syncStamina?.Value ?? false, 
				UpgradeType.ExtraJump => _syncExtraJump?.Value ?? false, 
				UpgradeType.TumbleLaunch => _syncTumbleLaunch?.Value ?? false, 
				UpgradeType.MapPlayerCount => _syncMapPlayerCount?.Value ?? false, 
				UpgradeType.SprintSpeed => _syncSprintSpeed?.Value ?? false, 
				UpgradeType.GrabStrength => _syncGrabStrength?.Value ?? false, 
				UpgradeType.GrabRange => _syncGrabRange?.Value ?? false, 
				UpgradeType.GrabThrow => _syncGrabThrow?.Value ?? false, 
				_ => _moddedUpgrades?.Value ?? false, 
			};
		}
	}
	public class UpgradeId
	{
		public UpgradeType Type { get; } = SyncUtil.GetUpgradeType(rawName);


		public string RawName { get; }

		public UpgradeId(string rawName)
		{
			RawName = rawName;
			base..ctor();
		}

		public UpgradeId(UpgradeType upgradeType)
			: this(SyncUtil.GetUpgradeName(upgradeType))
		{
			Type = upgradeType;
		}
	}
	public enum UpgradeType
	{
		Modded,
		Health,
		Stamina,
		ExtraJump,
		TumbleLaunch,
		MapPlayerCount,
		SprintSpeed,
		GrabStrength,
		GrabRange,
		GrabThrow
	}
}