Decompiled source of CustomPaintings v2.0.0

CustomPaintings.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CustomPainting.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks.Data;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CustomPaintings")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2c2ae5266a60c7cbdb62c065a0824cf1acd48c47")]
[assembly: AssemblyProduct("CustomPaintings")]
[assembly: AssemblyTitle("CustomPaintings")]
[assembly: AssemblyVersion("1.0.0.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 CustomPainting
{
	internal class CustomPaintingsModule : CustomPaintingsModuleBase
	{
		private readonly string[] _paintingFiles;

		private Random? _randomForTextureSelection;

		private readonly Dictionary<int, int> _paintingValues = new Dictionary<int, int>();

		internal CustomPaintingsModule(IEnumerable<string> paintingFiles, ManualLogSource logger)
			: base(logger)
		{
			_paintingFiles = paintingFiles.ToArray();
		}

		internal override void SetMaterialVariantsForPainting(Item paintingItem)
		{
			if (_paintingFiles.Length != 0)
			{
				Logger.LogInfo((object)"Updating textures");
				Material baseMaterialVariant = paintingItem.materialVariants[0];
				paintingItem.materialVariants = ((IEnumerable<string>)_paintingFiles).Select((Func<string, Material>)delegate(string f)
				{
					//IL_0003: Unknown result type (might be due to invalid IL or missing references)
					//IL_0009: Expected O, but got Unknown
					//IL_001c: 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_002a: Expected O, but got Unknown
					Texture2D val = new Texture2D(2, 2);
					ImageConversion.LoadImage(val, File.ReadAllBytes(f));
					return new Material(baseMaterialVariant)
					{
						mainTexture = (Texture)(object)val
					};
				}).ToArray();
				for (int i = 0; i < _paintingFiles.Length; i++)
				{
					Logger.LogDebug((object)$"File #{i} = {_paintingFiles[i]}");
				}
				Logger.LogInfo((object)"Textures updated");
			}
		}

		internal override void GenerateTextureIndexForPainting(GrabbableObject painting)
		{
			int value;
			if (!((NetworkBehaviour)StartOfRound.Instance).IsServer && StartOfRound.Instance.shipHasLanded)
			{
				Logger.LogDebug((object)$"Generating and applying skipped (IsServer = {((NetworkBehaviour)StartOfRound.Instance).IsServer} shipHasLanded = {StartOfRound.Instance.shipHasLanded})");
			}
			else if (!_paintingValues.TryGetValue(((Object)painting).GetInstanceID(), out value))
			{
				if (_randomForTextureSelection == null)
				{
					SetSeedForTextureIndexGenerator(StartOfRound.Instance.randomMapSeed);
				}
				value = _randomForTextureSelection.Next(_paintingFiles.Length);
				_paintingValues[((Object)painting).GetInstanceID()] = value;
				ApplyMaterial(painting, value);
				Logger.LogInfo((object)$"Generated fileIndex ({value}) for object {((Component)painting).gameObject} ({((Object)painting).GetInstanceID()})");
			}
		}

		private void ApplyMaterial(GrabbableObject painting, int fileIndex)
		{
			if (fileIndex < 0)
			{
				Logger.LogError((object)$"Incorrect fileIndex: {fileIndex}");
				return;
			}
			if (fileIndex >= painting.itemProperties.materialVariants.Length)
			{
				Logger.LogError((object)$"FileIndex ({fileIndex}) is greater then available materials ({painting.itemProperties.materialVariants.Length})");
				return;
			}
			((Renderer)((Component)painting).gameObject.GetComponent<MeshRenderer>()).sharedMaterial = painting.itemProperties.materialVariants[fileIndex];
			Logger.LogInfo((object)$"Applied fileIndex {fileIndex} texture");
		}

		internal override void ApplyTexturesForPaintingsInSpawnedScrap(NetworkObjectReference[] spawnedScrap)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			NetworkObject val2 = default(NetworkObject);
			for (int i = 0; i < spawnedScrap.Length; i++)
			{
				NetworkObjectReference val = spawnedScrap[i];
				if (!((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null))
				{
					continue;
				}
				GrabbableObject component = ((Component)val2).GetComponent<GrabbableObject>();
				if (component.IsPainting())
				{
					if (!_paintingValues.TryGetValue(((Object)component).GetInstanceID(), out var value))
					{
						Logger.LogWarning((object)$"Cannot find file index for object {((Component)component).gameObject} ({((Object)component).GetInstanceID()})");
					}
					else
					{
						ApplyMaterial(component, value);
					}
				}
			}
		}

		internal override void SetSeedForTextureIndexGenerator(int roundSeed)
		{
			int num = roundSeed + 300;
			Logger.LogDebug((object)$"Used seed {num}");
			_randomForTextureSelection = new Random(num);
		}

		internal override int GetPaintingFileIndex(GrabbableObject painting)
		{
			if (_paintingValues.TryGetValue(((Object)painting).GetInstanceID(), out var value))
			{
				Logger.LogInfo((object)$"FileIndex ({value}) for object {((Component)painting).gameObject} ({((Object)painting).GetInstanceID()})");
				return value;
			}
			Logger.LogWarning((object)$"Dictionary doesn't contain fileIndex for object {((Component)painting).gameObject} ({((Object)painting).GetInstanceID()})");
			return 0;
		}

		internal override void LoadPaintingTextureByIndex(GrabbableObject painting, int fileIndex)
		{
			if (fileIndex < 0)
			{
				Logger.LogError((object)$"Incorrect fileIndex: {fileIndex}");
				return;
			}
			if (fileIndex > _paintingFiles.Length)
			{
				Logger.LogWarning((object)$"FileIndex ({fileIndex}) is greater than files count ({_paintingFiles.Length})");
				return;
			}
			_paintingValues[((Object)painting).GetInstanceID()] = fileIndex;
			ApplyMaterial(painting, fileIndex);
			Logger.LogInfo((object)$"Loaded fileIndex ({fileIndex}) for object {((Component)painting).gameObject} ({((Object)painting).GetInstanceID()})");
		}

		internal override void ClearSelectedTextures()
		{
			_paintingValues.Clear();
			_randomForTextureSelection = null;
		}
	}
	internal abstract class CustomPaintingsModuleBase
	{
		protected readonly ManualLogSource Logger;

		protected CustomPaintingsModuleBase(ManualLogSource logger)
		{
			Logger = logger;
			base..ctor();
		}

		internal bool CheckLobbyOrPatchIt()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			bool result = false;
			GameNetworkManager instance = GameNetworkManager.Instance;
			Lobby? val = ((instance != null) ? instance.currentLobby : null);
			Lobby value;
			if (!val.HasValue)
			{
				Logger.LogDebug((object)"Lobby not created");
			}
			else if (((NetworkBehaviour)StartOfRound.Instance).IsServer)
			{
				value = val.Value;
				((Lobby)(ref value)).SetData("CustomPaintings", bool.TrueString);
				result = true;
			}
			else
			{
				value = val.Value;
				string data = ((Lobby)(ref value)).GetData("CustomPaintings");
				if (data == bool.TrueString)
				{
					result = true;
					Logger.LogDebug((object)"Mod enabled");
				}
				else
				{
					Logger.LogDebug((object)"Host doesn't use mod");
				}
			}
			return result;
		}

		internal void EnableSavingDataForPainting(Item paintingItem)
		{
			paintingItem.saveItemVariable = true;
		}

		internal abstract void SetMaterialVariantsForPainting(Item paintingItem);

		internal abstract void GenerateTextureIndexForPainting(GrabbableObject painting);

		internal abstract void ApplyTexturesForPaintingsInSpawnedScrap(NetworkObjectReference[] spawnedScrap);

		internal abstract void SetSeedForTextureIndexGenerator(int roundSeed);

		internal abstract int GetPaintingFileIndex(GrabbableObject painting);

		internal abstract void LoadPaintingTextureByIndex(GrabbableObject painting, int fileIndex);

		internal abstract void ClearSelectedTextures();
	}
	[BepInPlugin("Boniato.CustomPaintings", "CustomPaintings", "2.0.0")]
	public class CustomPaintingsPatcher : BaseUnityPlugin
	{
		private const string ModGuid = "Boniato.CustomPaintings";

		private const string ModName = "CustomPaintings";

		private const string ModVersion = "2.0.0";

		private readonly Harmony _harmony = new Harmony("Boniato.CustomPaintings");

		private static CustomPaintingsPatcher? Instance;

		private static CustomPaintingsModuleBase? _module;

		private static Config? _config;

		private void Awake()
		{
			if (Instance == null)
			{
				Instance = this;
			}
			ManualLogSource val = Logger.CreateLogSource("Boniato.CustomPaintings");
			_config = new Config(((BaseUnityPlugin)this).Config);
			IEnumerable<string> enumerable = new TextureFilesLoader(_config, val).Load();
			if (enumerable.Any())
			{
				_module = new CustomPaintingsModule(enumerable, val);
				val.LogDebug((object)"Used real module");
			}
			else
			{
				_module = new DummyCustomPaintingsModule(val);
				val.LogDebug((object)"Used dummy module");
			}
			_harmony.PatchAll(typeof(CustomPaintingsPatcher));
			val.LogInfo((object)"------- CustomPaintings loaded -------");
		}

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPrefix]
		private static void FindAndPatchPaintingItemWhenGameLoaded(StartOfRound __instance)
		{
			bool flag = _module?.CheckLobbyOrPatchIt() ?? false;
			Item val = __instance.allItemsList.itemsList.First((Item i) => i.itemName == "Painting");
			if (flag)
			{
				_module?.EnableSavingDataForPainting(val);
			}
			_module?.SetMaterialVariantsForPainting(val);
		}

		[HarmonyPatch(typeof(GrabbableObject), "Start")]
		[HarmonyPrefix]
		private static void GenerateTextureIndexForPaintingOnObjectStart(GrabbableObject __instance)
		{
			if (__instance.IsPainting())
			{
				_module?.GenerateTextureIndexForPainting(__instance);
			}
		}

		[HarmonyPatch(typeof(RoundManager), "SyncScrapValuesClientRpc")]
		[HarmonyPostfix]
		private static void ApplyTexturesForPaintingsInSpawnedScrapOnSyncScrapValues(NetworkObjectReference[] spawnedScrap)
		{
			_module?.ApplyTexturesForPaintingsInSpawnedScrap(spawnedScrap);
		}

		[HarmonyPatch(typeof(RoundManager), "GenerateNewLevelClientRpc")]
		[HarmonyPrefix]
		private static void SetSeedForTextureIndexGeneratorOnLevelStart(int randomSeed)
		{
			_module?.SetSeedForTextureIndexGenerator(randomSeed);
		}

		[HarmonyPatch(typeof(GrabbableObject), "GetItemDataToSave")]
		[HarmonyPostfix]
		private static void GetPaintingFileIndexToSave(GrabbableObject __instance, ref int __result)
		{
			if (__instance.IsPainting())
			{
				__result = _module?.GetPaintingFileIndex(__instance) ?? __result;
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "LoadItemSaveData")]
		[HarmonyPostfix]
		private static void LoadPaintingTextureBySaveData(GrabbableObject __instance, int saveData)
		{
			if (__instance.IsPainting())
			{
				_module?.LoadPaintingTextureByIndex(__instance, saveData);
			}
		}

		[HarmonyPatch(typeof(GameNetworkManager), "ResetGameValuesToDefault")]
		[HarmonyPostfix]
		public static void ClearSelectedTexturesAfterEndGame()
		{
			_module?.ClearSelectedTextures();
		}
	}
	internal class DummyCustomPaintingsModule : CustomPaintingsModuleBase
	{
		public DummyCustomPaintingsModule(ManualLogSource logger)
			: base(logger)
		{
		}

		internal override void SetMaterialVariantsForPainting(Item paintingItem)
		{
		}

		internal override void GenerateTextureIndexForPainting(GrabbableObject painting)
		{
		}

		internal override void ApplyTexturesForPaintingsInSpawnedScrap(NetworkObjectReference[] spawnedScrap)
		{
		}

		internal override void SetSeedForTextureIndexGenerator(int roundSeed)
		{
		}

		internal override int GetPaintingFileIndex(GrabbableObject painting)
		{
			return 0;
		}

		internal override void LoadPaintingTextureByIndex(GrabbableObject painting, int fileIndex)
		{
		}

		internal override void ClearSelectedTextures()
		{
		}
	}
	public static class Extensions
	{
		public static bool IsPainting(this GrabbableObject obj)
		{
			return obj.itemProperties.itemName == "Painting";
		}
	}
	internal class TextureFilesLoader
	{
		private const string DefaultTextureNameFormat = "default{0}.png";

		private static readonly string[] DefaultTexturesUrls = new string[8] { "https://i.imgur.com/ePiClDl.png", "https://i.imgur.com/yZCdjxh.png", "https://i.imgur.com/bIb8wXG.png", "https://i.imgur.com/kl1QiwG.png", "https://i.imgur.com/2kHSzn6.png", "https://i.imgur.com/rvdZYBH.png", "https://i.imgur.com/tgJinSI.png", "https://i.imgur.com/ZullTxg.png" };

		private readonly ManualLogSource _logger;

		private readonly Config _config;

		internal TextureFilesLoader(Config config, ManualLogSource logger)
		{
			_logger = logger;
			_config = config;
		}

		internal IEnumerable<string> Load()
		{
			string text = PrepareDefaultDirectory();
			PrepareWebTextures(text);
			IEnumerable<string> directories = PrepareAdditionalTextureDirectories(text);
			return GetTextureFiles(directories).ToArray();
		}

		private IEnumerable<string> PrepareUrls()
		{
			List<string> list = new List<string>();
			if (_config.DefaultPaintings.Value)
			{
				list.AddRange(DefaultTexturesUrls);
			}
			if (!string.IsNullOrEmpty(_config.CustomUrls.Value))
			{
				list.AddRange((from v in _config.CustomUrls.Value.Split(',')
					select v.Trim()).ToArray());
			}
			return list;
		}

		private static string PrepareDefaultDirectory()
		{
			string fullPath = Path.GetFullPath(Path.Combine(Paths.PluginPath, "CustomPaintings"));
			if (!Directory.Exists(fullPath))
			{
				Directory.CreateDirectory(fullPath);
			}
			return fullPath;
		}

		private IEnumerable<string> PrepareAdditionalTextureDirectories(string defaultDirectory)
		{
			string defaultDirectory2 = defaultDirectory;
			List<string> list = Directory.GetDirectories(Paths.PluginPath, "CustomPaintings", SearchOption.AllDirectories).ToList();
			list.AddRange(Directory.GetDirectories(Paths.PluginPath, "paintings", SearchOption.AllDirectories));
			list.AddRange(from d in _config.AdditionalDirectories.Value.Split(',')
				select d.Trim() into d
				select Path.IsPathRooted(d) ? d : Path.Combine(Paths.BepInExRootPath, d));
			list = (from d in (from d in list.Select(Path.GetFullPath)
					where !d.Equals(defaultDirectory2, StringComparison.InvariantCultureIgnoreCase)
					select d).Distinct()
				orderby d
				select d).ToList();
			list.Insert(0, defaultDirectory2);
			return list;
		}

		private void PrepareWebTextures(string directoryDownloadTo)
		{
			bool flag = _config.ForceDownload.Value;
			if (_config.DefaultPaintings.Value)
			{
				flag |= !CheckDefaultTextureExisting(directoryDownloadTo);
			}
			else
			{
				DeleteDefaultTextures(directoryDownloadTo);
			}
			IEnumerable<string> enumerable = PrepareUrls();
			if (flag | (Directory.GetFiles(directoryDownloadTo).Length < enumerable.Count()))
			{
				DownloadTextures(directoryDownloadTo, enumerable);
			}
		}

		private static bool CheckDefaultTextureExisting(string directory)
		{
			for (int i = 0; i < DefaultTexturesUrls.Length; i++)
			{
				if (!File.Exists(Path.Combine(directory, $"default{i}.png")))
				{
					return false;
				}
			}
			return true;
		}

		private void DownloadTextures(string directory, IEnumerable<string> urls)
		{
			int num = 1;
			foreach (string url in urls)
			{
				HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
				HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
				if (httpWebResponse.StatusCode == HttpStatusCode.OK)
				{
					string path = ((!_config.DefaultPaintings.Value) ? $"{directory}/custom{num}.png" : ((num <= DefaultTexturesUrls.Length) ? $"{directory}/default{num}.png" : $"{directory}/custom{num - DefaultTexturesUrls.Length - 1}.png"));
					using FileStream destination = new FileStream(path, FileMode.Create);
					httpWebResponse.GetResponseStream()?.CopyTo(destination);
				}
				else if (_config.DefaultPaintings.Value)
				{
					if (num <= DefaultTexturesUrls.Length)
					{
						_logger.LogWarning((object)$"Error downloading default image {num}");
					}
					else
					{
						_logger.LogWarning((object)$"Error downloading custom image {num - 8}");
					}
				}
				else
				{
					_logger.LogWarning((object)$"Error downloading custom image {num}");
				}
				num++;
			}
		}

		private static void DeleteDefaultTextures(string directory)
		{
			for (int i = 1; i <= DefaultTexturesUrls.Length; i++)
			{
				File.Delete($"{directory}/default{i}.png");
			}
		}

		private static IEnumerable<string> GetTextureFiles(IEnumerable<string> directories)
		{
			List<string> list = new List<string>();
			string[] source = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" };
			foreach (string directory in directories)
			{
				if (!Directory.Exists(directory))
				{
					continue;
				}
				foreach (string file in Directory.EnumerateFiles(directory))
				{
					if (source.Any((string t) => file.EndsWith(t)))
					{
						list.Add(file);
					}
				}
			}
			list.Sort();
			return list;
		}
	}
}
namespace CustomPainting.Configuration
{
	public class Config
	{
		internal const string DefaultDirectory = "CustomPaintings";

		public ConfigEntry<string> AdditionalDirectories;

		[Obsolete("Use now AdditionalDirectories")]
		public ConfigEntry<string> Directory;

		public ConfigEntry<string> CustomUrls;

		public ConfigEntry<bool> DefaultPaintings;

		public ConfigEntry<bool> ForceDownload;

		public Config(ConfigFile cfg)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0033: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_0082: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00b2: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00de: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_010a: Expected O, but got Unknown
			Directory = cfg.Bind<string>(new ConfigDefinition("General", "Directory"), string.Empty, new ConfigDescription("Deprecated! Use AdditionalDirectories now. Directory in which the mod will search for the paintings (using BepInEx as root, use / as separator)", (AcceptableValueBase)null, Array.Empty<object>()));
			AdditionalDirectories = cfg.Bind<string>(new ConfigDefinition("General", "Additional directories"), string.IsNullOrEmpty(Directory.Value) ? "plugins/CustomPaintings" : Directory.Value, new ConfigDescription("List of directories split by semi for painting textures search", (AcceptableValueBase)null, Array.Empty<object>()));
			CustomUrls = cfg.Bind<string>(new ConfigDefinition("General", "Custom Urls"), string.Empty, new ConfigDescription("Custom urls of the images to download them as default (separate them with commas, for example: https://i.imgur.com/ePiClDl.png,https://i.imgur.com/yZCdjxh.png)", (AcceptableValueBase)null, Array.Empty<object>()));
			DefaultPaintings = cfg.Bind<bool>(new ConfigDefinition("General", "Default Paintings"), true, new ConfigDescription("Enable it to use the default paintings of famous works", (AcceptableValueBase)null, Array.Empty<object>()));
			ForceDownload = cfg.Bind<bool>(new ConfigDefinition("General", "Force Download"), false, new ConfigDescription("Enable it to download the url images in every launch (if it is false, the mod will download the textures only when creates the directory)", (AcceptableValueBase)null, Array.Empty<object>()));
		}
	}
}