Decompiled source of MineLab v1.0.0

Bonelab/Mods/Bonecraft.dll

Decompiled 20 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Net.WebSockets;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using BoneLib;
using BoneLib.BoneMenu;
using Bonecraft;
using Bonecraft.Geo;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Data;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Data;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(BonecraftMod), "Bonecraft", "0.4.0", "Dante", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Bonecraft")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Bonecraft")]
[assembly: AssemblyTitle("Bonecraft")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 Bonecraft
{
	internal sealed class BlockLook
	{
		private const string StandardShader = "SLZ/LitMAS/LitMAS Standard";

		private const string CrackShader = "SLZ/Mod2x";

		private static readonly string[] LitShaders = new string[2] { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit" };

		private static readonly string[] UnlitShaders = new string[1] { "Universal Render Pipeline/Unlit" };

		private static readonly string[] Maps = new string[9] { "_BumpMap", "_MetallicGlossMap", "_OcclusionMap", "_EmissionMap", "_DetailAlbedoMap", "_DetailNormalMap", "_DetailMask", "_ParallaxMap", "_SpecGlossMap" };

		private static readonly string[] Keywords = new string[9] { "_NORMALMAP", "_EMISSION", "_METALLICSPECGLOSSMAP", "_DETAIL_MULX2", "_PARALLAXMAP", "_OCCLUSIONMAP", "_ALPHATEST_ON", "_ALPHABLEND_ON", "_SURFACE_TYPE_TRANSPARENT" };

		private const HideFlags Keep = (HideFlags)32;

		private static readonly (string[] words, string block)[] Guesses = new(string[], string)[12]
		{
			(new string[3] { "grass", "lawn", "turf" }, "minecraft:grass_block"),
			(new string[5] { "dirt", "soil", "mud", "earth", "ground" }, "minecraft:dirt"),
			(new string[3] { "sand", "desert", "beach" }, "minecraft:sand"),
			(new string[1] { "gravel" }, "minecraft:gravel"),
			(new string[2] { "snow", "ice" }, "minecraft:snow_block"),
			(new string[1] { "brick" }, "minecraft:bricks"),
			(new string[5] { "wood", "plank", "timber", "crate", "log" }, "minecraft:oak_planks"),
			(new string[2] { "glass", "window" }, "minecraft:glass"),
			(new string[6] { "metal", "steel", "iron", "pipe", "vent", "grate" }, "minecraft:iron_block"),
			(new string[3] { "tile", "marble", "lab" }, "minecraft:smooth_stone"),
			(new string[2] { "lava", "magma" }, "minecraft:magma_block"),
			(new string[8] { "concrete", "cement", "rock", "stone", "cliff", "cave", "wall", "floor" }, "minecraft:stone")
		};

		private readonly Link link;

		private readonly Instance log;

		private readonly Dictionary<string, Material> materials = new Dictionary<string, Material>();

		private readonly Dictionary<string, List<Material>> waiting = new Dictionary<string, List<Material>>();

		private readonly Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();

		private readonly HashSet<string> requested = new HashSet<string>();

		private readonly HashSet<string> seeThroughKeys = new HashSet<string>();

		private readonly HashSet<string> missing = new HashSet<string>();

		private Material template;

		private Texture2D roughMas;

		private bool warnedCracks;

		public BlockLook(Link link, Instance log)
		{
			this.link = link;
			this.log = log;
			link.Connected += Reask;
		}

		private void Reask()
		{
			requested.RemoveWhere((string k) => !textures.ContainsKey(k) && !missing.Contains(k));
			foreach (string key in waiting.Keys)
			{
				if (requested.Add(key))
				{
					link.Send(Json.Obj("t", "tex", "block", key));
				}
			}
		}

		public void Reset()
		{
			materials.Clear();
			waiting.Clear();
			template = null;
		}

		public void SetTemplate(Material m)
		{
			if ((Object)(object)template == (Object)null && (Object)(object)m != (Object)null && (Object)(object)m.shader != (Object)null && m.renderQueue < 2450)
			{
				template = m;
			}
		}

		public Material For(string key)
		{
			return Get(key, key, 1f);
		}

		public Material Particle(string key)
		{
			return Get(key + "#particle", key, 0.6f);
		}

		public Material Crack(int stage)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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)
			//IL_006a: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			string key = "destroy:" + stage;
			if (materials.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Shader val = Shader.Find("SLZ/Mod2x");
			if ((Object)(object)val != (Object)null)
			{
				value = new Material(val)
				{
					name = "Bonecraft crack " + stage,
					hideFlags = (HideFlags)32
				};
				SetColor(value, "_Color", Color.white);
				SetFloat(value, "_Multiplier", 1f);
				SetFloat(value, "_alpha", 1f);
				SetFloat(value, "_AlphaCutoff", 0f);
				SetFloat(value, "_OffsetFactor", -1f);
				SetFloat(value, "_OffsetUnits", -1f);
			}
			else
			{
				value = Glow("SLZ/LitMAS/LitMAS Standard", "Bonecraft crack " + stage, 1f);
				if ((Object)(object)value == (Object)null)
				{
					if (!warnedCracks)
					{
						warnedCracks = true;
						log.Warning("no shader for cracks in this level: no crack animation");
					}
					return null;
				}
				Blend(value, 2, 3);
			}
			materials[key] = value;
			Texture(key, value);
			return value;
		}

		private Material Glow(string shaderName, string name, float shade)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0028: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			Shader val = Shader.Find(shaderName);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Material val2 = new Material(val)
			{
				name = name,
				hideFlags = (HideFlags)32
			};
			SetColor(val2, "_BaseColor", new Color(0f, 0f, 0f, 1f));
			SetFloat(val2, "_Emission", 1f);
			val2.EnableKeyword("_EMISSION");
			SetColor(val2, "_EmissionColor", new Color(shade, shade, shade, 1f));
			Matte(val2);
			return val2;
		}

		private static void Blend(Material m, int src, int dst)
		{
			SetFloat(m, "_Surface", 1f);
			m.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
			SetFloat(m, "_BlendSrc", src);
			SetFloat(m, "_BlendDst", dst);
			SetFloat(m, "_ZWrite", 0f);
			m.renderQueue = 3000;
		}

		private Material Get(string id, string textureKey, float shade)
		{
			if (materials.TryGetValue(id, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = Create(textureKey, shade);
			((Object)value).hideFlags = (HideFlags)32;
			materials[id] = value;
			Texture(textureKey, value);
			return value;
		}

		private void Texture(string key, Material m)
		{
			if (textures.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				Apply(m, value);
				if (seeThroughKeys.Contains(key) && m.renderQueue < 2450)
				{
					Blend(m, 5, 10);
				}
				return;
			}
			if (!waiting.TryGetValue(key, out var value2))
			{
				value2 = (waiting[key] = new List<Material>());
			}
			value2.Add(m);
			if (requested.Add(key))
			{
				link.Send(Json.Obj("t", "tex", "block", key));
			}
		}

		public unsafe void OnTexture(string key, int w, int h, string rgba)
		{
			//IL_0091: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			if (rgba == null || w <= 0 || h <= 0)
			{
				log.Warning("Minecraft has no texture for " + key);
				missing.Add(key);
				return;
			}
			byte[] array = Convert.FromBase64String(rgba);
			if (key.StartsWith("destroy:"))
			{
				for (int i = 0; i + 3 < array.Length; i += 4)
				{
					if (array[i + 3] < 26)
					{
						int num = i;
						int num2 = i + 1;
						byte b;
						array[i + 2] = (b = 128);
						array[num] = (array[num2] = b);
					}
					array[i + 3] = byte.MaxValue;
				}
			}
			Texture2D val = new Texture2D(w, h, (TextureFormat)4, false)
			{
				name = "Bonecraft " + key,
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)0,
				hideFlags = (HideFlags)32
			};
			fixed (byte* ptr = array)
			{
				val.LoadRawTextureData((IntPtr)ptr, array.Length);
			}
			val.Apply(false);
			textures[key] = val;
			bool flag = key.Contains('|') && SeeThrough(array);
			if (flag)
			{
				seeThroughKeys.Add(key);
			}
			if (!waiting.Remove(key, out var value))
			{
				return;
			}
			foreach (Material item in value)
			{
				if ((Object)(object)item != (Object)null)
				{
					Apply(item, val);
					if (flag && item.renderQueue < 2450)
					{
						Blend(item, 5, 10);
					}
				}
			}
		}

		private static void Apply(Material m, Texture2D t)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			m.mainTexture = (Texture)(object)t;
			string[] array = new string[3] { "_BaseMap", "_MainTex", "_EmissionMap" };
			foreach (string text in array)
			{
				if (m.HasProperty(text))
				{
					m.SetTexture(text, (Texture)(object)t);
					m.SetTextureScale(text, Vector2.one);
					m.SetTextureOffset(text, Vector2.zero);
				}
			}
		}

		private static bool SeeThrough(byte[] rgba)
		{
			int num = 0;
			for (int i = 3; i < rgba.Length; i += 4)
			{
				if (rgba[i] < 200)
				{
					num++;
				}
			}
			return num > rgba.Length / 4 / 20;
		}

		private Material Create(string key, float shade)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			Shader val = (BonecraftMod.Settings.UnlitBlocks.Value ? null : Shader.Find("SLZ/LitMAS/LitMAS Standard"));
			bool flag = (Object)(object)template != (Object)null && ((Object)template.shader).name.Contains("Triplanar", StringComparison.OrdinalIgnoreCase);
			Material val2;
			if ((Object)(object)val != (Object)null)
			{
				val2 = new Material(val);
			}
			else if ((Object)(object)template != (Object)null && !BonecraftMod.Settings.UnlitBlocks.Value && (!flag || (Object)(object)Find(LitShaders) == (Object)null))
			{
				val2 = new Material(template);
				string[] maps = Maps;
				foreach (string text in maps)
				{
					if (val2.HasProperty(text))
					{
						val2.SetTexture(text, (Texture)null);
					}
				}
				maps = Keywords;
				foreach (string text2 in maps)
				{
					val2.DisableKeyword(text2);
				}
				SetColor(val2, "_EmissionColor", Color.black);
				log.Msg($"{key}: a copy of {((Object)template).name} ({((Object)template.shader).name})");
			}
			else
			{
				Shader val3 = Find(BonecraftMod.Settings.UnlitBlocks.Value ? UnlitShaders : LitShaders) ?? Find(UnlitShaders);
				if ((Object)(object)val3 == (Object)null)
				{
					throw new InvalidOperationException("no shader for blocks yet (none of the URP shaders is loaded)");
				}
				val2 = new Material(val3);
				log.Msg(key + ": " + ((Object)val3).name);
			}
			((Object)val2).name = "Bonecraft " + key;
			float num = BonecraftMod.Settings.BlockBrightness.Value * shade;
			Color c = default(Color);
			((Color)(ref c))..ctor(num, num, num, 1f);
			SetColor(val2, "_BaseColor", c);
			SetColor(val2, "_Color", c);
			Matte(val2);
			return val2;
		}

		private unsafe void Matte(Material m)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			if ((Object)(object)roughMas == (Object)null)
			{
				roughMas = new Texture2D(1, 1, (TextureFormat)4, false, true)
				{
					name = "Bonecraft rough MAS",
					hideFlags = (HideFlags)32
				};
				byte[] array = new byte[4] { 0, 255, 0, 255 };
				fixed (byte* ptr = array)
				{
					roughMas.LoadRawTextureData((IntPtr)ptr, array.Length);
				}
				roughMas.Apply(false);
			}
			string[] array2 = new string[3] { "_MetallicGlossMap", "_MASMap", "_MAS" };
			foreach (string text in array2)
			{
				if (m.HasProperty(text))
				{
					m.SetTexture(text, (Texture)(object)roughMas);
				}
			}
			SetFloat(m, "_Metallic", 0f);
			SetFloat(m, "_Smoothness", 0f);
			SetFloat(m, "_Glossiness", 0f);
			SetFloat(m, "_GlossMapScale", 0f);
			SetFloat(m, "_SpecularHighlights", 0f);
			SetFloat(m, "_EnvironmentReflections", 0f);
			m.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
			m.EnableKeyword("_ENVIRONMENTREFLECTIONS_OFF");
		}

		private static Shader Find(string[] names)
		{
			for (int i = 0; i < names.Length; i++)
			{
				Shader val = Shader.Find(names[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static void SetColor(Material m, string p, Color c)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (m.HasProperty(p))
			{
				m.SetColor(p, c);
			}
		}

		private static void SetFloat(Material m, string p, float f)
		{
			if (m.HasProperty(p))
			{
				m.SetFloat(p, f);
			}
		}

		public static string Guess(Collider c, Renderer surface)
		{
			string text = Match(((Object)((Component)c).gameObject).name + " " + (((Object)(object)c.sharedMaterial != (Object)null) ? ((Object)c.sharedMaterial).name : ""));
			if (text != null)
			{
				return text;
			}
			if ((Object)(object)surface != (Object)null)
			{
				foreach (Material item in (Il2CppArrayBase<Material>)(object)surface.sharedMaterials)
				{
					if ((Object)(object)item != (Object)null && (text = Match(((Object)item).name + " " + ((Object)((Component)surface).gameObject).name)) != null)
					{
						return text;
					}
				}
			}
			return "minecraft:stone";
		}

		private static string Match(string names)
		{
			string text = names.ToLowerInvariant();
			(string[], string)[] guesses = Guesses;
			for (int i = 0; i < guesses.Length; i++)
			{
				(string[], string) tuple = guesses[i];
				string[] item = tuple.Item1;
				string item2 = tuple.Item2;
				string[] array = item;
				foreach (string value in array)
				{
					if (text.Contains(value))
					{
						return item2;
					}
				}
			}
			return null;
		}
	}
	internal sealed class Blocks
	{
		public sealed class Info
		{
			public float Hardness = 1.5f;

			public float Resistance = 6f;

			public string Hit;

			public string Break;

			public float Volume = 1f;

			public float Pitch = 1f;

			public bool Known;
		}

		private readonly Link link;

		private readonly Dictionary<string, Info> infos = new Dictionary<string, Info>();

		private readonly HashSet<string> asked = new HashSet<string>();

		public Blocks(Link link)
		{
			this.link = link;
			link.Connected += delegate
			{
				asked.RemoveWhere((string id) => !infos.TryGetValue(id, out var value) || !value.Known);
			};
		}

		public Info InfoFor(string id)
		{
			if (!infos.TryGetValue(id, out var value))
			{
				value = (infos[id] = new Info());
			}
			if (!value.Known && asked.Add(id))
			{
				link.Send(Json.Obj("t", "blockinfo", "block", id));
			}
			return value;
		}

		public void OnInfo(JsonElement m)
		{
			string key = m.GetProperty("block").GetString();
			if (!infos.TryGetValue(key, out var value))
			{
				value = (infos[key] = new Info());
			}
			if (m.TryGetProperty("hardness", out var value2))
			{
				value.Hardness = value2.GetSingle();
				value.Resistance = m.GetProperty("resistance").GetSingle();
				value.Known = true;
			}
			if (m.TryGetProperty("sound", out var value3))
			{
				value.Hit = value3.GetProperty("hit").GetString();
				value.Break = value3.GetProperty("break").GetString();
				value.Volume = value3.GetProperty("volume").GetSingle();
				value.Pitch = value3.GetProperty("pitch").GetSingle();
			}
		}
	}
	public sealed class BonecraftMod : MelonMod
	{
		internal static Settings Settings;

		private Link link;

		private PunchDetector left;

		private PunchDetector right;

		private Digger digger;

		private Weapons weapons;

		private MinecraftScreen screen;

		private bool levelReady;

		private bool keysBroken;

		private bool reportedRunning;

		private bool menuBuilt;

		private float nextRejectLog;

		public override void OnInitializeMelon()
		{
			Settings = new Settings();
			ClassInjector.RegisterTypeInIl2Cpp<HitRelay>();
			link = new Link(Settings.Url.Value, ((MelonBase)this).LoggerInstance);
			link.Start();
			left = new PunchDetector("L");
			right = new PunchDetector("R");
			digger = new Digger(link, ((MelonBase)this).LoggerInstance);
			weapons = new Weapons(digger, ((MelonBase)this).LoggerInstance);
			Hooking.OnLevelLoaded += delegate(LevelInfo info)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				OnLevel(info.title);
			};
			Hooking.OnLevelLoading += delegate
			{
				levelReady = false;
			};
			Hooking.OnLevelUnloaded += delegate
			{
				levelReady = false;
			};
		}

		private void OnLevel(string title)
		{
			levelReady = true;
			reportedRunning = false;
			digger.Reset();
			weapons.Reset();
			left.Reset();
			right.Reset();
			link.Send(Json.Obj("t", "hello", "game", "bonelab", "level", title));
			BuildMenu();
			if (Settings.ScreenEnabled.Value)
			{
				if (screen == null)
				{
					screen = new MinecraftScreen(Settings.ShmName.Value, ((MelonBase)this).LoggerInstance);
				}
				screen.Spawn();
			}
		}

		private void BuildMenu()
		{
			//IL_002c: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			if (menuBuilt || Page.Root == null)
			{
				return;
			}
			try
			{
				Settings s = Settings;
				Page obj = Page.Root.CreatePage("Bonecraft", Color.green, 0, true);
				obj.CreateBool("Bonecraft On", Color.green, s.Enabled.Value, (Action<bool>)delegate(bool v)
				{
					Set(s.Enabled, v);
				});
				obj.CreateBool("Fists Break Blocks", Color.white, s.FistsBreak.Value, (Action<bool>)delegate(bool v)
				{
					Set(s.FistsBreak, v);
				});
				obj.CreateBool("Guns Break Blocks", Color.white, s.GunsBreak.Value, (Action<bool>)delegate(bool v)
				{
					Set(s.GunsBreak, v);
				});
				obj.CreateBool("Melee Breaks Blocks", Color.white, s.MeleeBreaks.Value, (Action<bool>)delegate(bool v)
				{
					Set(s.MeleeBreaks, v);
				});
				obj.CreateBool("Physics Breaks Blocks", Color.white, s.PhysicsBreaks.Value, (Action<bool>)delegate(bool v)
				{
					Set(s.PhysicsBreaks, v);
				});
				obj.CreateFloat("Gun Damage x", Color.yellow, s.GunDamage.Value, 0.25f, 0f, 5f, (Action<float>)delegate(float v)
				{
					Set(s.GunDamage, v);
				});
				obj.CreateFloat("Melee Damage x", Color.yellow, s.MeleeDamage.Value, 0.25f, 0f, 5f, (Action<float>)delegate(float v)
				{
					Set(s.MeleeDamage, v);
				});
				obj.CreateFloat("Physics Damage x", Color.yellow, s.PhysicsDamage.Value, 0.25f, 0f, 5f, (Action<float>)delegate(float v)
				{
					Set(s.PhysicsDamage, v);
				});
				obj.CreateFloat("Block Sound Volume", Color.cyan, s.SoundVolume.Value, 0.1f, 0f, 2f, (Action<float>)delegate(float v)
				{
					Set(s.SoundVolume, v);
				});
				menuBuilt = true;
				((MelonBase)this).LoggerInstance.Msg("BoneMenu → Bonecraft: settings");
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("BoneMenu: " + ex.Message);
			}
		}

		private static void Set<T>(MelonPreferences_Entry<T> entry, T value)
		{
			entry.Value = value;
			Settings.Save();
		}

		public override void OnFixedUpdate()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (levelReady && !((Object)(object)Player.Head == (Object)null))
			{
				if (!reportedRunning)
				{
					reportedRunning = true;
					((MelonBase)this).LoggerInstance.Msg("hands found: left " + (((Object)(object)Player.LeftHand != (Object)null) ? "yes" : "NO") + ", right " + (((Object)(object)Player.RightHand != (Object)null) ? "yes" : "NO"));
				}
				weapons.FixedUpdate();
				Vector3 position = Player.Head.position;
				float fixedDeltaTime = Time.fixedDeltaTime;
				Detect(left, Player.LeftHand, position, fixedDeltaTime);
				Detect(right, Player.RightHand, position, fixedDeltaTime);
			}
		}

		private void Detect(PunchDetector detector, Hand hand, Vector3 head, float dt)
		{
			//IL_0039: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hand == (Object)null)
			{
				return;
			}
			bool holding = Settings.IgnoreWhileHolding.Value && (Object)(object)Player.GetObjectInHand(hand) != (Object)null;
			float curl = Curl(hand);
			Punch punch;
			string rejected;
			bool num = detector.Step(((Component)hand).transform.position, head, dt, holding, curl, out punch, out rejected);
			if (rejected != null && Settings.LogRejected.Value && Time.unscaledTime >= nextRejectLog)
			{
				nextRejectLog = Time.unscaledTime + 0.5f;
				((MelonBase)this).LoggerInstance.Msg("no punch (" + detector.Hand + "): " + rejected);
			}
			if (num && Settings.Enabled.Value && Settings.FistsBreak.Value)
			{
				if (Settings.LogPunches.Value)
				{
					((MelonBase)this).LoggerInstance.Msg($"punch {punch.Hand} {punch.Speed:0.0} m/s on {punch.ObjectName} at {punch.Point}");
				}
				digger.Punched(in punch);
			}
		}

		private static float Curl(Hand hand)
		{
			BaseController controller = hand.Controller;
			if ((Object)(object)controller == (Object)null)
			{
				return 1f;
			}
			return Mathf.Min(Mathf.Min(controller.GetIndexCurlAxis(), controller.GetMiddleCurlAxis()), Mathf.Min(controller.GetRingCurlAxis(), controller.GetPinkyCurlAxis()));
		}

		public override void OnUpdate()
		{
			if (levelReady)
			{
				digger.Update();
			}
			foreach (string item in link.Drain())
			{
				if (Settings.LogPunches.Value && item.StartsWith("{\"t\":\"mined\""))
				{
					((MelonBase)this).LoggerInstance.Msg("minecraft: " + ((item.Length > 160) ? (item.Substring(0, 160) + "…") : item));
				}
				try
				{
					digger.OnMessage(item);
				}
				catch (Exception value)
				{
					((MelonBase)this).LoggerInstance.Error($"handling {((item.Length > 120) ? item.Substring(0, 120) : item)}: {value}");
				}
			}
			if (keysBroken || !levelReady)
			{
				return;
			}
			try
			{
				if (Input.GetKeyDown((KeyCode)290))
				{
					screen?.PlaceInFrontOfPlayer();
				}
				if (Input.GetKeyDown((KeyCode)291))
				{
					screen?.Toggle();
				}
			}
			catch (Exception ex)
			{
				keysBroken = true;
				((MelonBase)this).LoggerInstance.Warning("hotkeys disabled: " + ex.Message);
			}
		}

		public override void OnLateUpdate()
		{
			if (levelReady)
			{
				screen?.Tick();
				digger.LateUpdate();
			}
		}

		public override void OnDeinitializeMelon()
		{
			link?.Dispose();
			screen?.Dispose();
		}
	}
	internal sealed class Carver
	{
		public sealed class CellCut
		{
			public readonly Surfaces Removed = new Surfaces();

			public int Pending;

			public float Started;

			public bool Done => Pending <= 0;
		}

		private sealed class CutRenderer
		{
			public MeshRenderer Source;

			public MeshFilter Target;

			public MeshCopy Copy;

			public Mesh Written;

			public MeshReader.PendingRead Pending;

			public bool Batched;

			public bool Failed;

			public bool Surface;

			public readonly List<(Box box, CellCut job)> Holes = new List<(Box, CellCut)>();

			public readonly HashSet<Vector3> Applied = new HashSet<Vector3>();
		}

		private sealed class CutCollider
		{
			public MeshCollider Target;

			public MeshCopy Copy;

			public MeshReader.PendingRead Pending;

			public bool Failed;

			public readonly List<Box> Holes = new List<Box>();
		}

		private sealed class BoxSet
		{
			public BoxCollider Original;

			public GameObject Holder;

			public List<Box> Boxes;

			public Matrix4x4 WorldToLocal;

			public PhysicMaterial Material;
		}

		public const string CarvePrefix = "Bonecraft carve";

		private static readonly string[] NotSurfaces = new string[11]
		{
			"occluder", "collider", "col_", "shadow", "sky", "inverted_sphere", "volume", "fog", "light", "beam",
			"glow"
		};

		private readonly Instance log;

		private readonly Dictionary<IntPtr, CutRenderer> cuts = new Dictionary<IntPtr, CutRenderer>();

		private readonly Dictionary<IntPtr, BoxSet> boxSets = new Dictionary<IntPtr, BoxSet>();

		private readonly Dictionary<IntPtr, MeshCopy> meshColliders = new Dictionary<IntPtr, MeshCopy>();

		private readonly Dictionary<IntPtr, CutCollider> gpuColliders = new Dictionary<IntPtr, CutCollider>();

		private readonly Dictionary<IntPtr, HashSet<Vector3>> colliderHoles = new Dictionary<IntPtr, HashSet<Vector3>>();

		private readonly HashSet<IntPtr> warned = new HashSet<IntPtr>();

		private readonly HashSet<IntPtr> known = new HashSet<IntPtr>();

		private List<(MeshRenderer r, Box b)> renderers;

		private float indexedAt = -100f;

		public Carver(Instance log)
		{
			this.log = log;
		}

		public void Reset()
		{
			cuts.Clear();
			boxSets.Clear();
			meshColliders.Clear();
			gpuColliders.Clear();
			colliderHoles.Clear();
			warned.Clear();
			known.Clear();
			renderers = null;
		}

		public bool Known(Collider c)
		{
			return known.Contains(((Il2CppObjectBase)c).Pointer);
		}

		public CellCut Carve(in Box cell)
		{
			//IL_00bc: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			CellCut cellCut = new CellCut
			{
				Started = Time.unscaledTime
			};
			int num = 0;
			int num2 = 0;
			foreach (var item2 in Candidates(cell))
			{
				MeshRenderer item = item2.Item1;
				try
				{
					if (CarveRenderer(item, in cell, cellCut))
					{
						num++;
					}
				}
				catch (Exception ex)
				{
					Warn(((Il2CppObjectBase)item).Pointer, "couldn't cut mesh of " + ((Object)((Component)item).gameObject).name + ": " + ex.Message);
				}
			}
			Vector3 center = cell.Center;
			Vector3 v = cell.Size * 0.5f + new Vector3(0.01f);
			foreach (Collider item3 in (Il2CppArrayBase<Collider>)(object)Physics.OverlapBox(U.V3(center), U.V3(v), Quaternion.identity, -1, (QueryTriggerInteraction)1))
			{
				if ((Object)(object)item3 == (Object)null || (Object)(object)item3.attachedRigidbody != (Object)null || ((Object)((Component)item3).gameObject).name.StartsWith("Bonecraft wall"))
				{
					continue;
				}
				known.Add(((Il2CppObjectBase)item3).Pointer);
				try
				{
					if (CarveCollider(item3, in cell))
					{
						num2++;
					}
				}
				catch (Exception ex2)
				{
					Warn(((Il2CppObjectBase)item3).Pointer, "couldn't cut collider " + ((Object)((Component)item3).gameObject).name + ": " + ex2.Message);
				}
			}
			log.Msg($"carved cell {cell.Min}: {num} mesh(es), {num2} collider(s)");
			return cellCut;
		}

		public void Prefetch(in Box region)
		{
			foreach (var item2 in Candidates(region))
			{
				MeshRenderer item = item2.Item1;
				if (cuts.ContainsKey(((Il2CppObjectBase)item).Pointer))
				{
					continue;
				}
				try
				{
					CutRenderer cutRenderer = Begin(item);
					if (cutRenderer != null)
					{
						cuts[((Il2CppObjectBase)item).Pointer] = cutRenderer;
					}
				}
				catch (Exception ex)
				{
					Warn(((Il2CppObjectBase)item).Pointer, "couldn't read mesh of " + ((Object)((Component)item).gameObject).name + ": " + ex.Message);
				}
			}
		}

		public bool Ready(in Box region)
		{
			foreach (var item2 in Candidates(region))
			{
				MeshRenderer item = item2.Item1;
				if (cuts.TryGetValue(((Il2CppObjectBase)item).Pointer, out var value) && value.Pending != null)
				{
					return false;
				}
			}
			return true;
		}

		public TriangleField Field(in Box region)
		{
			TriangleField triangleField = new TriangleField();
			foreach (var item2 in Candidates(region))
			{
				MeshRenderer item = item2.Item1;
				if (!cuts.TryGetValue(((Il2CppObjectBase)item).Pointer, out var value) || value.Copy == null || !value.Surface)
				{
					continue;
				}
				foreach (var (vector, vector2, vector3) in value.Copy.Original)
				{
					if (new Box(Vector3.Min(vector, Vector3.Min(vector2, vector3)), Vector3.Max(vector, Vector3.Max(vector2, vector3))).Overlaps(in region))
					{
						triangleField.Add(vector, vector2, vector3);
					}
				}
			}
			return triangleField;
		}

		public void Update()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			GpuRead.Pump();
			UpdateColliders();
			List<IntPtr> list = null;
			foreach (var (item, cutRenderer2) in cuts)
			{
				if ((Object)(object)cutRenderer2.Source == (Object)null)
				{
					(list ?? (list = new List<IntPtr>())).Add(item);
					Release(cutRenderer2);
				}
				else
				{
					if (cutRenderer2.Pending == null || !cutRenderer2.Pending.Done)
					{
						continue;
					}
					MeshReader.PendingRead pending = cutRenderer2.Pending;
					cutRenderer2.Pending = null;
					try
					{
						cutRenderer2.Copy = pending.Finish();
						CheckBounds(cutRenderer2);
						ApplyHoles(cutRenderer2);
					}
					catch (Exception ex)
					{
						cutRenderer2.Failed = true;
						cutRenderer2.Copy = null;
						float value = BonecraftMod.Settings.BlockSize.Value;
						float num = Largest(U.Box(((Renderer)cutRenderer2.Source).bounds));
						bool flag = ((Object)((Component)cutRenderer2.Source).gameObject).name.Contains("decal", StringComparison.OrdinalIgnoreCase);
						bool flag2 = cutRenderer2.Holes.Count > 0 && (num <= 1.5f * value || (flag && num <= 3f * value));
						if (flag2)
						{
							((Renderer)cutRenderer2.Source).enabled = false;
						}
						Warn(((Il2CppObjectBase)cutRenderer2.Source).Pointer, $"couldn't cut mesh of {((Object)((Component)cutRenderer2.Source).gameObject).name}: {ex.Message}{(flag2 ? " (small: hidden instead)" : "")}");
						Release(cutRenderer2);
					}
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (IntPtr item2 in list)
			{
				cuts.Remove(item2);
			}
		}

		private static void Release(CutRenderer cut)
		{
			foreach (var hole in cut.Holes)
			{
				CellCut item = hole.job;
				if (item != null)
				{
					item.Pending--;
				}
			}
			cut.Holes.Clear();
		}

		private static void CheckBounds(CutRenderer cut)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			Box box = U.Box(((Renderer)cut.Source).bounds);
			Box bounds = cut.Copy.Bounds;
			float num = 0.1f + 0.05f * box.Size.Length();
			if ((bounds.Min - box.Min).Length() > num || (bounds.Max - box.Max).Length() > num)
			{
				throw new InvalidOperationException($"read-back geometry {bounds.Min}..{bounds.Max} isn't where it draws ({box.Min}..{box.Max}); left as it is");
			}
		}

		public MeshRenderer SurfaceAt(Vector3 p)
		{
			Box region = new Box(p - new Vector3(0.05f), p + new Vector3(0.05f));
			MeshRenderer result = null;
			float num = float.MaxValue;
			foreach (var item3 in Candidates(region))
			{
				MeshRenderer item = item3.Item1;
				Box item2 = item3.Item2;
				float volume = item2.Volume;
				if (volume < num && IsSurface(item))
				{
					result = item;
					num = volume;
				}
			}
			return result;
		}

		private IEnumerable<(MeshRenderer, Box)> Candidates(Box region)
		{
			if (renderers == null || Time.unscaledTime - indexedAt > 15f)
			{
				Index();
			}
			foreach (var (val, item) in renderers)
			{
				if (item.Overlaps(in region, 0.01f) && (Object)(object)val != (Object)null && (((Renderer)val).enabled || cuts.ContainsKey(((Il2CppObjectBase)val).Pointer)))
				{
					yield return (val, item);
				}
			}
		}

		private void Index()
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			indexedAt = Time.unscaledTime;
			renderers = new List<(MeshRenderer, Box)>();
			Transform val = (((Object)(object)Player.RigManager != (Object)null) ? ((Component)Player.RigManager).transform : null);
			foreach (MeshRenderer item in Object.FindObjectsOfType<MeshRenderer>())
			{
				if (!((Object)(object)item == (Object)null) && (((Renderer)item).enabled || cuts.ContainsKey(((Il2CppObjectBase)item).Pointer)) && !((Object)((Component)item).gameObject).name.StartsWith("Bonecraft") && !((Object)(object)((Component)item).GetComponentInParent<Rigidbody>() != (Object)null) && (!((Object)(object)val != (Object)null) || !((Component)item).transform.IsChildOf(val)))
				{
					renderers.Add((item, U.Box(((Renderer)item).bounds)));
				}
			}
		}

		private static bool IsSurface(MeshRenderer r)
		{
			string text = ((Object)((Component)r).gameObject).name.ToLowerInvariant();
			string[] notSurfaces = NotSurfaces;
			foreach (string value in notSurfaces)
			{
				if (text.Contains(value))
				{
					return false;
				}
			}
			foreach (Material item in (Il2CppArrayBase<Material>)(object)((Renderer)r).sharedMaterials)
			{
				if ((Object)(object)item == (Object)null || item.renderQueue >= 2450)
				{
					return false;
				}
			}
			return true;
		}

		private bool CarveRenderer(MeshRenderer r, in Box cell, CellCut job)
		{
			if (!cuts.TryGetValue(((Il2CppObjectBase)r).Pointer, out var value))
			{
				value = Begin(r);
				if (value == null)
				{
					return false;
				}
				cuts[((Il2CppObjectBase)r).Pointer] = value;
			}
			if (value.Failed || !value.Applied.Add(cell.Min))
			{
				return false;
			}
			job.Pending++;
			value.Holes.Add((cell, job));
			if (value.Copy != null)
			{
				ApplyHoles(value);
			}
			return true;
		}

		private void ApplyHoles(CutRenderer cut)
		{
			bool flag = false;
			foreach (var hole2 in cut.Holes)
			{
				Box hole = hole2.box;
				CellCut item = hole2.job;
				flag |= cut.Copy.Subtract(in hole, item?.Removed);
				if (item != null)
				{
					item.Pending--;
				}
			}
			cut.Holes.Clear();
			if (flag)
			{
				if ((Object)(object)cut.Target == (Object)null)
				{
					Install(cut);
				}
				Mesh written = cut.Written;
				cut.Written = cut.Copy.Build("Bonecraft cut " + ((Object)((Component)cut.Source).gameObject).name);
				cut.Target.sharedMesh = cut.Written;
				if ((Object)(object)written != (Object)null)
				{
					Object.Destroy((Object)(object)written);
				}
				log.Msg("cut " + ((Object)((Component)cut.Source).gameObject).name);
			}
		}

		private CutRenderer Begin(MeshRenderer r)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			MeshFilter component = ((Component)r).GetComponent<MeshFilter>();
			Mesh val = (((Object)(object)component != (Object)null) ? component.sharedMesh : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (!((Renderer)r).isPartOfStaticBatch)
			{
				return new CutRenderer
				{
					Source = r,
					Target = component,
					Surface = IsSurface(r),
					Pending = MeshReader.BeginGpu(val, U.M(((Renderer)r).localToWorldMatrix), 0, val.subMeshCount)
				};
			}
			Transform staticBatchRootTransform = ((Renderer)r).staticBatchRootTransform;
			Matrix4x4 rootToWorld = (((Object)(object)staticBatchRootTransform != (Object)null) ? U.M(staticBatchRootTransform.localToWorldMatrix) : Matrix4x4.Identity);
			int length = ((Il2CppArrayBase<Material>)(object)((Renderer)r).sharedMaterials).Length;
			var (num, subCount) = BatchRange(val, r, length, in rootToWorld);
			if (num < 0)
			{
				Warn(((Il2CppObjectBase)r).Pointer, "couldn't find " + ((Object)((Component)r).gameObject).name + " in its static batch");
				return null;
			}
			return new CutRenderer
			{
				Source = r,
				Batched = true,
				Surface = IsSurface(r),
				Pending = MeshReader.BeginGpu(val, in rootToWorld, num, subCount)
			};
		}

		private static void Install(CutRenderer cut)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			MeshRenderer source = cut.Source;
			Transform staticBatchRootTransform = ((Renderer)source).staticBatchRootTransform;
			GameObject val = new GameObject("Bonecraft cut " + ((Object)((Component)source).gameObject).name)
			{
				layer = ((Component)source).gameObject.layer
			};
			if ((Object)(object)staticBatchRootTransform != (Object)null)
			{
				val.transform.SetParent(staticBatchRootTransform, false);
			}
			MeshFilter target = val.AddComponent<MeshFilter>();
			MeshRenderer val2 = val.AddComponent<MeshRenderer>();
			((Renderer)val2).sharedMaterials = ((Renderer)source).sharedMaterials;
			((Renderer)val2).shadowCastingMode = ((Renderer)source).shadowCastingMode;
			((Renderer)val2).receiveShadows = ((Renderer)source).receiveShadows;
			((Renderer)val2).lightProbeUsage = ((Renderer)source).lightProbeUsage;
			((Renderer)val2).reflectionProbeUsage = ((Renderer)source).reflectionProbeUsage;
			((Renderer)val2).probeAnchor = ((Renderer)source).probeAnchor;
			((Renderer)val2).renderingLayerMask = ((Renderer)source).renderingLayerMask;
			((Renderer)val2).rendererPriority = ((Renderer)source).rendererPriority;
			((Renderer)val2).lightmapIndex = ((Renderer)source).lightmapIndex;
			((Renderer)val2).lightmapScaleOffset = (Vector4)(BakedLightmapUvs(cut.Copy, ((Renderer)source).lightmapScaleOffset) ? new Vector4(1f, 1f, 0f, 0f) : ((Renderer)source).lightmapScaleOffset);
			MaterialPropertyBlock val3 = new MaterialPropertyBlock();
			((Renderer)source).GetPropertyBlock(val3);
			((Renderer)val2).SetPropertyBlock(val3);
			cut.Target = target;
			ReplaceInLods((Renderer)(object)source, (Renderer)(object)val2);
			((Renderer)source).enabled = false;
		}

		private static void ReplaceInLods(Renderer from, Renderer to)
		{
			try
			{
				LODGroup componentInParent = ((Component)from).GetComponentInParent<LODGroup>();
				if ((Object)(object)componentInParent == (Object)null)
				{
					return;
				}
				Il2CppReferenceArray<LOD> lODs = componentInParent.GetLODs();
				bool flag = false;
				for (int i = 0; i < ((Il2CppArrayBase<LOD>)(object)lODs).Length; i++)
				{
					Il2CppReferenceArray<Renderer> val = ((Il2CppArrayBase<LOD>)(object)lODs)[i].renderers;
					int num = 0;
					while (val != null && num < ((Il2CppArrayBase<Renderer>)(object)val).Length)
					{
						if ((Object)(object)((Il2CppArrayBase<Renderer>)(object)val)[num] != (Object)null && ((Il2CppObjectBase)((Il2CppArrayBase<Renderer>)(object)val)[num]).Pointer == ((Il2CppObjectBase)from).Pointer)
						{
							((Il2CppArrayBase<Renderer>)(object)val)[num] = to;
							flag = true;
						}
						num++;
					}
				}
				if (flag)
				{
					componentInParent.SetLODs(lODs);
				}
			}
			catch
			{
			}
		}

		private static bool BakedLightmapUvs(MeshCopy copy, Vector4 st)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			(Vector2, Vector2)? tuple = copy.LightmapUvRange();
			if (!tuple.HasValue || (MathF.Abs(st.x - 1f) < 0.0001f && MathF.Abs(st.y - 1f) < 0.0001f && MathF.Abs(st.z) < 0.0001f && MathF.Abs(st.w) < 0.0001f))
			{
				return false;
			}
			var (vector, vector2) = tuple.Value;
			if (vector.X >= st.z - 0.002f && vector.Y >= st.w - 0.002f && vector2.X <= st.z + st.x + 0.002f)
			{
				return vector2.Y <= st.w + st.y + 0.002f;
			}
			return false;
		}

		private static (int first, int count) BatchRange(Mesh mesh, MeshRenderer r, int materials, in Matrix4x4 rootToWorld)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			int subMeshCount = mesh.subMeshCount;
			int num = -1;
			try
			{
				num = r.subMeshStartIndex;
			}
			catch
			{
			}
			if (num >= 0 && num < subMeshCount)
			{
				Box box = U.Box(((Renderer)r).bounds);
				float num2 = 0.02f + 0.01f * box.Size.Length();
				Vector3 vector = new Vector3(float.MaxValue);
				Vector3 vector2 = new Vector3(float.MinValue);
				for (int i = 0; i < materials && num + i < subMeshCount; i++)
				{
					Box box2 = SubBox(mesh, num + i, in rootToWorld);
					vector = Vector3.Min(vector, box2.Min);
					vector2 = Vector3.Max(vector2, box2.Max);
					if ((vector - box.Min).Length() < num2 && (vector2 - box.Max).Length() < num2)
					{
						return (first: num, count: i + 1);
					}
				}
				return (first: num, count: Math.Max(1, Math.Min(materials, subMeshCount - num)));
			}
			return (first: FindBatchRange(mesh, (Renderer)(object)r, materials, in rootToWorld), count: materials);
		}

		private static Box SubBox(Mesh mesh, int s, in Matrix4x4 rootToWorld)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = U.Box(mesh.GetSubMesh(s)._bounds_k__BackingField).Corners();
			for (int i = 0; i < 8; i++)
			{
				array[i] = U.Point(in rootToWorld, array[i]);
			}
			return Box.Around(array);
		}

		private static int FindBatchRange(Mesh mesh, Renderer r, int count, in Matrix4x4 rootToWorld)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Box box = U.Box(r.bounds);
			float num = 0.02f + 0.01f * box.Size.Length();
			int subMeshCount = mesh.subMeshCount;
			Box[] array = new Box[subMeshCount];
			for (int i = 0; i < subMeshCount; i++)
			{
				array[i] = SubBox(mesh, i, in rootToWorld);
			}
			for (int j = 0; j + count <= subMeshCount; j++)
			{
				Vector3 vector = array[j].Min;
				Vector3 vector2 = array[j].Max;
				for (int k = 1; k < count; k++)
				{
					vector = Vector3.Min(vector, array[j + k].Min);
					vector2 = Vector3.Max(vector2, array[j + k].Max);
				}
				if ((vector - box.Min).Length() < num && (vector2 - box.Max).Length() < num)
				{
					return j;
				}
			}
			return -1;
		}

		private bool CarveCollider(Collider col, in Box cell)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			if (((Object)((Component)col).gameObject).name == "Bonecraft carve")
			{
				Transform parent = ((Component)col).transform.parent;
				if ((Object)(object)parent != (Object)null && boxSets.TryGetValue(((Il2CppObjectBase)((Component)parent).gameObject).Pointer, out var value))
				{
					return Subtract(value, in cell);
				}
				return false;
			}
			BoxCollider val = ((Il2CppObjectBase)col).TryCast<BoxCollider>();
			if ((Object)(object)val != (Object)null)
			{
				if (!boxSets.TryGetValue(((Il2CppObjectBase)val).Pointer, out var value2))
				{
					Vector3 vector = U.N3(val.center);
					Vector3 vector2 = U.N3(val.size) * 0.5f;
					Box piece = new Box(vector - vector2, vector + vector2);
					Matrix4x4 worldToLocal = U.M(((Component)val).transform.worldToLocalMatrix);
					if (!Clip.Cuts(in piece, LocalHole(in worldToLocal, in cell)))
					{
						return false;
					}
					GameObject val2 = new GameObject("Bonecraft carve")
					{
						layer = ((Component)val).gameObject.layer
					};
					val2.transform.SetParent(((Component)val).transform, false);
					value2 = new BoxSet
					{
						Original = val,
						Holder = val2,
						Boxes = new List<Box> { piece },
						WorldToLocal = worldToLocal,
						Material = ((Collider)val).sharedMaterial
					};
					boxSets[((Il2CppObjectBase)val).Pointer] = value2;
					boxSets[((Il2CppObjectBase)val2).Pointer] = value2;
				}
				return Subtract(value2, in cell);
			}
			MeshCollider val3 = ((Il2CppObjectBase)col).TryCast<MeshCollider>();
			if ((Object)(object)val3 != (Object)null)
			{
				Mesh sharedMesh = val3.sharedMesh;
				if ((Object)(object)sharedMesh == (Object)null)
				{
					return false;
				}
				if (!sharedMesh.isReadable)
				{
					return CarveGpuCollider(val3, sharedMesh, in cell);
				}
				if (!meshColliders.TryGetValue(((Il2CppObjectBase)val3).Pointer, out var value3))
				{
					value3 = MeshReader.ReadCpu(sharedMesh, U.M(((Component)val3).transform.localToWorldMatrix));
					meshColliders[((Il2CppObjectBase)val3).Pointer] = value3;
				}
				if (!Holes((Collider)(object)val3).Add(cell.Min) || !value3.Subtract(in cell))
				{
					return false;
				}
				Mesh sharedMesh2 = value3.Build("Bonecraft cut collider " + ((Object)((Component)val3).gameObject).name);
				val3.convex = false;
				val3.sharedMesh = sharedMesh2;
				return true;
			}
			if ((Object)(object)((Il2CppObjectBase)col).TryCast<TerrainCollider>() != (Object)null)
			{
				Warn(((Il2CppObjectBase)col).Pointer, "terrain " + ((Object)((Component)col).gameObject).name + " can't be dug yet; it stays solid");
			}
			return false;
		}

		private HashSet<Vector3> Holes(Collider c)
		{
			if (!colliderHoles.TryGetValue(((Il2CppObjectBase)c).Pointer, out var value))
			{
				value = (colliderHoles[((Il2CppObjectBase)c).Pointer] = new HashSet<Vector3>());
			}
			return value;
		}

		private bool CarveGpuCollider(MeshCollider mc, Mesh mesh, in Box cell)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!gpuColliders.TryGetValue(((Il2CppObjectBase)mc).Pointer, out var value))
			{
				value = new CutCollider
				{
					Target = mc
				};
				try
				{
					value.Pending = MeshReader.BeginGpu(mesh, U.M(((Component)mc).transform.localToWorldMatrix), 0, mesh.subMeshCount);
				}
				catch (Exception ex)
				{
					value.Failed = true;
					NoCollisionCut(mc, ex.Message);
				}
				gpuColliders[((Il2CppObjectBase)mc).Pointer] = value;
			}
			if (value.Failed || !Holes((Collider)(object)mc).Add(cell.Min))
			{
				return false;
			}
			value.Holes.Add(cell);
			if (value.Copy != null)
			{
				ApplyColliderHoles(value);
			}
			return true;
		}

		private void UpdateColliders()
		{
			foreach (CutCollider value in gpuColliders.Values)
			{
				if (value.Pending == null || !value.Pending.Done || (Object)(object)value.Target == (Object)null)
				{
					continue;
				}
				MeshReader.PendingRead pending = value.Pending;
				value.Pending = null;
				try
				{
					MeshCopy copy = pending.Finish();
					string text = CheckCollider(value.Target, copy);
					if (text != null)
					{
						throw new InvalidOperationException(text);
					}
					value.Copy = copy;
					ApplyColliderHoles(value);
					log.Msg("cut collision of " + ((Object)((Component)value.Target).gameObject).name + " (read from the GPU, checked against the collider)");
				}
				catch (Exception ex)
				{
					value.Failed = true;
					value.Holes.Clear();
					NoCollisionCut(value.Target, ex.Message);
				}
			}
		}

		private static void ApplyColliderHoles(CutCollider cut)
		{
			bool flag = false;
			foreach (Box hole2 in cut.Holes)
			{
				Box hole = hole2;
				flag |= cut.Copy.Subtract(in hole);
			}
			cut.Holes.Clear();
			if (flag)
			{
				cut.Target.convex = false;
				cut.Target.sharedMesh = cut.Copy.Build("Bonecraft cut collider " + ((Object)((Component)cut.Target).gameObject).name);
			}
		}

		private void NoCollisionCut(MeshCollider mc, string why)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			float value = BonecraftMod.Settings.BlockSize.Value;
			bool flag = Largest(U.Box(((Collider)mc).bounds)) <= 1.5f * value;
			if (flag)
			{
				((Collider)mc).enabled = false;
			}
			Warn(((Il2CppObjectBase)mc).Pointer, $"collision of {((Object)((Component)mc).gameObject).name} can't be cut ({why}); {(flag ? "small: switched off" : "it stays solid")}");
		}

		private static float Largest(in Box b)
		{
			return MathF.Max(b.Size.X, MathF.Max(b.Size.Y, b.Size.Z));
		}

		private static string CheckCollider(MeshCollider mc, MeshCopy copy)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			Box box = U.Box(((Collider)mc).bounds);
			Box bounds = copy.Bounds;
			float num = 0.05f + 0.02f * box.Size.Length();
			if ((bounds.Min - box.Min).Length() > num || (bounds.Max - box.Max).Length() > num)
			{
				return $"read-back geometry {bounds.Min}..{bounds.Max} isn't the collider's ({box.Min}..{box.Max})";
			}
			List<(Vector3, Vector3, Vector3)> original = copy.Original;
			int num2 = Math.Min(64, original.Count);
			int num3 = 0;
			int num4 = 0;
			int mask = 1 << ((Component)mc).gameObject.layer;
			bool convex = mc.convex;
			for (int i = 0; i < num2; i++)
			{
				(Vector3, Vector3, Vector3) tuple = original[(int)((long)i * (long)original.Count / num2)];
				Vector3 item = tuple.Item1;
				Vector3 item2 = tuple.Item2;
				Vector3 item3 = tuple.Item3;
				Vector3 vector = Vector3.Cross(item2 - item, item3 - item);
				float num5 = vector.Length();
				if (num5 < 1E-06f)
				{
					continue;
				}
				vector /= num5;
				Vector3 vector2 = (item + item2 + item3) / 3f;
				num3++;
				bool num6;
				if (!convex)
				{
					if (Hits((Collider)(object)mc, vector2 + vector * 0.05f, -vector, mask))
					{
						goto IL_022a;
					}
					num6 = Hits((Collider)(object)mc, vector2 - vector * 0.05f, vector, mask);
				}
				else
				{
					num6 = (U.N3(((Collider)mc).ClosestPoint(U.V3(vector2))) - vector2).Length() < 0.02f;
				}
				if (!num6)
				{
					continue;
				}
				goto IL_022a;
				IL_022a:
				num4++;
			}
			if (num3 == 0)
			{
				return "nothing but degenerate triangles read back";
			}
			if (num4 * 4 < num3 * 3)
			{
				return $"read-back triangles aren't where the collider is ({num4} of {num3} probes found it)";
			}
			return null;
		}

		private static bool Hits(Collider target, Vector3 from, Vector3 dir, int mask)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			foreach (RaycastHit item in (Il2CppArrayBase<RaycastHit>)(object)Physics.RaycastAll(U.V3(from), U.V3(dir), 0.1f, mask, (QueryTriggerInteraction)1))
			{
				RaycastHit current = item;
				if ((Object)(object)((RaycastHit)(ref current)).collider != (Object)null && ((Il2CppObjectBase)((RaycastHit)(ref current)).collider).Pointer == ((Il2CppObjectBase)target).Pointer)
				{
					return true;
				}
			}
			return false;
		}

		private static Box LocalHole(in Matrix4x4 worldToLocal, in Box cell)
		{
			Vector3[] array = cell.Corners();
			for (int i = 0; i < 8; i++)
			{
				array[i] = U.Point(in worldToLocal, array[i]);
			}
			return Box.Around(array);
		}

		private static bool Subtract(BoxSet set, in Box cell)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: 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)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			Box hole = LocalHole(in set.WorldToLocal, in cell);
			List<Box> list = new List<Box>();
			bool flag = false;
			foreach (Box box in set.Boxes)
			{
				Box piece = box;
				if (Clip.Cuts(in piece, in hole))
				{
					flag = true;
					list.AddRange(Clip.SubtractBox(in piece, in hole));
				}
				else
				{
					list.Add(piece);
				}
			}
			if (!flag)
			{
				return false;
			}
			set.Boxes = list;
			for (int num = set.Holder.transform.childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)set.Holder.transform.GetChild(num)).gameObject);
			}
			foreach (Box item in list)
			{
				GameObject val = new GameObject("Bonecraft carve")
				{
					layer = set.Holder.layer
				};
				val.transform.SetParent(set.Holder.transform, false);
				BoxCollider obj = val.AddComponent<BoxCollider>();
				obj.center = U.V3(item.Center);
				obj.size = U.V3(item.Size);
				((Collider)obj).sharedMaterial = set.Material;
			}
			if ((Object)(object)set.Original != (Object)null && ((Collider)set.Original).enabled)
			{
				((Collider)set.Original).enabled = false;
			}
			return true;
		}

		private void Warn(IntPtr key, string message)
		{
			if (warned.Add(key))
			{
				log.Warning(message);
			}
		}
	}
	internal readonly record struct Cell(int G, int X, int Y, int Z)
	{
		public (int, int, int) Key => (X, Y, Z);

		public Cell(int G, int X, int Y, int Z)
		{
			this.G = G;
			this.X = X;
			this.Y = Y;
			this.Z = Z;
		}

		public Cell((int x, int y, int z) c)
			: this(0, c.x, c.y, c.z)
		{
		}

		public Cell Step(int face)
		{
			(int, int, int) tuple = Walls.Dirs[face];
			return new Cell(G, X + tuple.Item1, Y + tuple.Item2, Z + tuple.Item3);
		}

		public override string ToString()
		{
			return $"({X}, {Y}, {Z})";
		}
	}
	internal sealed class Digger
	{
		private sealed class Struck
		{
			public Cell Cell;

			public string Block;

			public int Layer;

			public Vector3 Point;

			public Vector3 Normal;
		}

		private sealed class Unfinished
		{
			public Cell Cell;

			public int Layer;

			public string Block;

			public Carver.CellCut Cut;

			public Box Region;
		}

		private const float Reach = 3f;

		private readonly Instance log;

		private readonly Link link;

		private readonly Carver carver;

		private readonly Walls walls;

		private readonly BlockLook look;

		private readonly Sounds sounds;

		private readonly Cracks cracks;

		private readonly Particles particles;

		private readonly Blocks blocks;

		private readonly HashSet<Cell> dug = new HashSet<Cell>();

		private readonly Dictionary<Cell, string> revealed = new Dictionary<Cell, string>();

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

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

		private readonly List<Unfinished> unfinished = new List<Unfinished>();

		private float nextRestoreCheck;

		private static float Size => BonecraftMod.Settings.BlockSize.Value;

		public Digger(Link link, Instance log)
		{
			this.log = log;
			this.link = link;
			carver = new Carver(log);
			look = new BlockLook(link, log);
			walls = new Walls(look);
			sounds = new Sounds(link, log);
			cracks = new Cracks(look);
			particles = new Particles(look);
			blocks = new Blocks(link);
		}

		public static (int, int, int) CellOf(Vector3 p)
		{
			float size = Size;
			return ((int)MathF.Floor(p.X / size), (int)MathF.Floor(p.Y / size), (int)MathF.Floor(p.Z / size));
		}

		public static Box BoxOf(Cell c)
		{
			float size = Size;
			Vector3 vector = new Vector3(c.X, c.Y, c.Z) * size;
			return new Box(vector, vector + new Vector3(size));
		}

		public void Reset()
		{
			dug.Clear();
			revealed.Clear();
			pending.Clear();
			recarves.Clear();
			unfinished.Clear();
			carver.Reset();
			walls.Reset();
			look.Reset();
			cracks.Reset();
			particles.Reset();
		}

		public void Update()
		{
			carver.Update();
			for (int num = this.unfinished.Count - 1; num >= 0; num--)
			{
				Unfinished unfinished = this.unfinished[num];
				if ((unfinished.Cut.Done && carver.Ready(in unfinished.Region)) || Time.unscaledTime - unfinished.Cut.Started > 2f)
				{
					this.unfinished.RemoveAt(num);
					try
					{
						Sides(unfinished);
					}
					catch (Exception value)
					{
						log.Error($"sides of {unfinished.Cell}: {value}");
					}
				}
			}
			cracks.Update();
			particles.Update(Time.deltaTime);
			if (Time.unscaledTime >= nextRestoreCheck)
			{
				nextRestoreCheck = Time.unscaledTime + 1f;
				RecarveRestored();
			}
		}

		public void LateUpdate()
		{
			particles.Render();
		}

		private void RecarveRestored()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			if (dug.Count == 0 || (Object)(object)Player.Head == (Object)null)
			{
				return;
			}
			Vector3 vector = U.N3(Player.Head.position);
			foreach (Cell item in dug)
			{
				Box cell = BoxOf(item);
				if ((cell.Center - vector).Length() > 40f)
				{
					continue;
				}
				Vector3 v = cell.Size * 0.5f - new Vector3(0.05f);
				foreach (Collider item2 in (Il2CppArrayBase<Collider>)(object)Physics.OverlapBox(U.V3(cell.Center), U.V3(v), Quaternion.identity, -1, (QueryTriggerInteraction)1))
				{
					if (Walls.Level(item2) && !(((Object)((Component)item2).gameObject).name == "Bonecraft carve") && !carver.Known(item2))
					{
						int value;
						int num = ((!recarves.TryGetValue(item, out value)) ? 1 : (value + 1));
						recarves[item] = num;
						if (num <= 3)
						{
							log.Msg($"level geometry came back in dug cell {item} ({((Object)((Component)item2).gameObject).name}): carving it again");
							carver.Carve(in cell);
						}
						break;
					}
				}
			}
		}

		private (Cell cell, string block) Target(Vector3 p, Vector3 n, Collider c)
		{
			Cell cell = new Cell(CellOf(p - n * (0.02f * Size)));
			if (dug.Contains(cell))
			{
				cell = new Cell(CellOf(p - n * (0.6f * Size)));
			}
			if (walls.OwnerOf(c, out var _) && revealed.TryGetValue(cell, out var value))
			{
				return (cell: cell, block: value);
			}
			MeshRenderer val = carver.SurfaceAt(p);
			if ((Object)(object)val != (Object)null)
			{
				foreach (Material item in (Il2CppArrayBase<Material>)(object)((Renderer)val).sharedMaterials)
				{
					look.SetTemplate(item);
				}
			}
			return (cell: cell, block: Materials.BlockFor(c, (Renderer)(object)val));
		}

		public void Hit(Vector3 p, Vector3 n, Collider c, string tool, float ticks, string hand)
		{
			if (BonecraftMod.Settings.Enabled.Value && !(ticks <= 0f))
			{
				var (cell, text) = Target(p, n, c);
				if (!dug.Contains(cell))
				{
					pending[cell.Key] = new Struck
					{
						Cell = cell,
						Block = text,
						Layer = ((Component)c).gameObject.layer,
						Point = p,
						Normal = n
					};
					link.Send(Json.Obj("t", "punch", "cell", new int[3] { cell.X, cell.Y, cell.Z }, "hint", text, "tool", tool, "ticks", ticks, "hand", hand));
				}
			}
		}

		public void Punched(in Punch punch)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			float ticks = Math.Clamp(8f * punch.Speed / 3f, 1f, 24f);
			Hit(U.N3(punch.Point), U.N3(punch.Normal), punch.Collider, "hand", ticks, punch.Hand);
		}

		public void Shot(Vector3 p, Vector3 n, Collider c, float ticks)
		{
			Hit(p, n, c, "best", ticks, "G");
		}

		public void OnMessage(string json)
		{
			using JsonDocument jsonDocument = JsonDocument.Parse(json);
			JsonElement rootElement = jsonDocument.RootElement;
			switch (rootElement.GetProperty("t").GetString())
			{
			case "tex":
				look.OnTexture(rootElement.GetProperty("block").GetString(), rootElement.GetProperty("w").GetInt32(), rootElement.GetProperty("h").GetInt32(), (rootElement.GetProperty("rgba").ValueKind == JsonValueKind.String) ? rootElement.GetProperty("rgba").GetString() : null);
				break;
			case "sound":
				sounds.OnSound(rootElement);
				break;
			case "blockinfo":
				blocks.OnInfo(rootElement);
				PreloadSounds(rootElement.GetProperty("block").GetString());
				break;
			case "mined":
			{
				if (rootElement.TryGetProperty("cell", out var value))
				{
					Mined(rootElement, (value[0].GetInt32(), value[1].GetInt32(), value[2].GetInt32()));
				}
				break;
			}
			}
		}

		private void PreloadSounds(string block)
		{
			Blocks.Info info = blocks.InfoFor(block);
			sounds.Preload(info.Hit);
			sounds.Preload(info.Break);
		}

		private void Mined(JsonElement m, (int, int, int) key)
		{
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			JsonElement value = m.GetProperty("block");
			string text = value.GetString();
			if (m.TryGetProperty("sound", out value))
			{
				bool num = blocks.InfoFor(text).Hit == null;
				blocks.OnInfo(m);
				if (num)
				{
					PreloadSounds(text);
				}
			}
			value = m.GetProperty("broken");
			bool boolean = value.GetBoolean();
			Struck value2;
			Struck value3;
			Struck struck = ((!boolean) ? (pending.TryGetValue(key, out value2) ? value2 : null) : (pending.Remove(key, out value3) ? value3 : null));
			if (struck == null)
			{
				return;
			}
			Blocks.Info info = blocks.InfoFor(text);
			Box box = BoxOf(struck.Cell);
			string key2 = text + "|particle";
			if (!boolean)
			{
				sounds.Play(info.Hit, U.V3(struck.Point), (info.Volume + 1f) / 8f, info.Pitch * 0.5f);
				particles.Crack(key2, struck.Point, struck.Normal, 6);
				if (!m.TryGetProperty("unbreakable", out value))
				{
					cracks.Show(struck.Cell, in box, struck.Point, struck.Normal, m.GetProperty("progress").GetSingle());
				}
			}
			else
			{
				cracks.Hide(struck.Cell);
				sounds.Play(info.Break, U.V3(box.Center), (info.Volume + 1f) / 2f, info.Pitch * 0.8f);
				particles.Destroy(key2, in box);
				Dig(struck.Cell, struck.Layer, text);
			}
		}

		private void Dig(Cell cell, int layer, string block)
		{
			if (dug.Add(cell))
			{
				Box cell2 = BoxOf(cell);
				for (int i = 0; i < 6; i++)
				{
					walls.Remove(cell.Step(i), Walls.Opposite(i));
				}
				Box region = new Box(cell2.Min - new Vector3(3f), cell2.Max + new Vector3(3f));
				carver.Prefetch(in region);
				Carver.CellCut cellCut = carver.Carve(in cell2);
				Unfinished unfinished = new Unfinished
				{
					Cell = cell,
					Layer = layer,
					Block = block,
					Cut = cellCut,
					Region = region
				};
				if (cellCut.Done && carver.Ready(in region))
				{
					Sides(unfinished);
				}
				else
				{
					this.unfinished.Add(unfinished);
				}
			}
		}

		private void Sides(Unfinished u)
		{
			Cell cell = u.Cell;
			Box cell2 = BoxOf(cell);
			TriangleField field = carver.Field(in u.Region);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			for (int i = 0; i < 6; i++)
			{
				Cell cell3 = cell.Step(i);
				if (dug.Contains(cell3))
				{
					continue;
				}
				bool[,] array = Walls.Sample(in cell2, i, (Vector3 q) => field.Classify(q, 3f) <= 0);
				if (i != 2)
				{
					num2 += SolidBelowLevel(in cell2, i, array);
				}
				int num4 = 0;
				bool[,] array2 = array;
				foreach (bool flag in array2)
				{
					num4 += (flag ? 1 : 0);
				}
				if (num4 == 0)
				{
					list.Add(Walls.Names[i]);
					continue;
				}
				if (num4 < 256)
				{
					list2.Add(Walls.Names[i]);
				}
				int covered;
				bool[,] draw = Uncovered(field, in cell2, i, array, out covered);
				num3 += covered;
				string value;
				string text = (revealed.TryGetValue(cell3, out value) ? value : u.Block);
				if (covered < num4)
				{
					revealed[cell3] = text;
				}
				try
				{
					walls.Build(cell, in cell2, i, array, draw, text + "|" + Walls.Names[Walls.Opposite(i)], u.Layer);
					num++;
				}
				catch (Exception ex)
				{
					log.Error($"side {Walls.Names[i]} of {cell}: {ex.Message}");
				}
			}
			log.Msg($"dug {cell} ({u.Block}): {num} block side(s){((list2.Count > 0) ? (" (" + string.Join(" ", list2) + " partly)") : "")}, open: {((list.Count > 0) ? string.Join(" ", list) : "none")} ({field.Count} level triangles around{((num2 > 0) ? $", {num2} sample(s) over the void under the level" : "")}{((num3 > 0) ? $", {num3} sample(s) still the level's own surface" : "")})");
		}

		private static bool[,] Uncovered(TriangleField field, in Box box, int face, bool[,] mask, out int covered)
		{
			(Vector3 n, Vector3 u, Vector3 v) tuple = Walls.Axes(face);
			Vector3 item = tuple.n;
			Vector3 item2 = tuple.u;
			Vector3 item3 = tuple.v;
			Vector3 vector = Walls.Origin(in box, face);
			float x = box.Size.X;
			float num = 0.005f;
			bool[,] array = new bool[16, 16];
			covered = 0;
			for (int i = 0; i < 16; i++)
			{
				for (int j = 0; j < 16; j++)
				{
					array[i, j] = mask[i, j];
					if (mask[i, j])
					{
						Vector3 vector2 = vector + item2 * (((float)i + 0.5f) / 16f * x) + item3 * (((float)j + 0.5f) / 16f * x);
						if (field.Cast(vector2 - item * num, face, num + 0.01f * x, out var _, out var front, out var normal) && front && Vector3.Dot(normal, -item) > 0.9f)
						{
							array[i, j] = false;
							covered++;
						}
					}
				}
			}
			return array;
		}

		private static int SolidBelowLevel(in Box box, int face, bool[,] mask)
		{
			float x = box.Size.X;
			int num = 0;
			if (face == 3)
			{
				if (BelowLevel(box.Center - Vector3.UnitY * (0.55f * x)))
				{
					for (int i = 0; i < 16; i++)
					{
						for (int j = 0; j < 16; j++)
						{
							num += ((!mask[i, j]) ? 1 : 0);
							mask[i, j] = true;
						}
					}
				}
				return num;
			}
			(Vector3 n, Vector3 u, Vector3 v) tuple = Walls.Axes(face);
			Vector3 item = tuple.n;
			Vector3 item2 = tuple.u;
			Vector3 item3 = tuple.v;
			Vector3 vector = Walls.Origin(in box, face);
			for (int k = 0; k < 16 && BelowLevel(vector + item2 * (0.5f * x) + item3 * (((float)k + 0.5f) / 16f * x) + item * (0.05f * x)); k++)
			{
				for (int l = 0; l < 16; l++)
				{
					num += ((!mask[l, k]) ? 1 : 0);
					mask[l, k] = true;
				}
			}
			return num;
		}

		private static bool BelowLevel(Vector3 q)
		{
			//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)
			//IL_0011: 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)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = U.V3(q);
			Vector3 val2 = U.V3(-Vector3.UnitY);
			RaycastHit val3 = default(RaycastHit);
			if (!Physics.Raycast(val, val2, ref val3, 500f, -1, (QueryTriggerInteraction)1))
			{
				return true;
			}
			if ((Object)(object)((RaycastHit)(ref val3)).collider != (Object)null && (Object)(object)((RaycastHit)(ref val3)).collider.attachedRigidbody == (Object)null)
			{
				return false;
			}
			foreach (RaycastHit item in (Il2CppArrayBase<RaycastHit>)(object)Physics.RaycastAll(val, val2, 500f, -1, (QueryTriggerInteraction)1))
			{
				RaycastHit current = item;
				if ((Object)(object)((RaycastHit)(ref current)).collider != (Object)null && (Object)(object)((RaycastHit)(ref current)).collider.attachedRigidbody == (Object)null)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class Cracks
	{
		private const float Heal = 5f;

		private readonly BlockLook look;

		private readonly Dictionary<Cell, (GameObject go, MeshRenderer r, int stage, float last)> active = new Dictionary<Cell, (GameObject, MeshRenderer, int, float)>();

		public Cracks(BlockLook look)
		{
			this.look = look;
		}

		public void Reset()
		{
			foreach (var value in active.Values)
			{
				if ((Object)(object)value.go != (Object)null)
				{
					Object.Destroy((Object)(object)value.go);
				}
			}
			active.Clear();
		}

		public void Show(Cell cell, in Box box, Vector3 point, Vector3 normal, float progress)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			int num = Math.Clamp((int)(progress * 10f), 0, 9);
			Material val = look.Crack(num);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (active.TryGetValue(cell, out (GameObject, MeshRenderer, int, float) value) && (Object)(object)value.Item1 != (Object)null)
			{
				if (value.Item3 != num)
				{
					((Renderer)value.Item2).sharedMaterial = val;
				}
				active[cell] = (value.Item1, value.Item2, num, Time.unscaledTime);
				return;
			}
			Mesh val2 = Overlay(in box, point, normal);
			if (!((Object)(object)val2 == (Object)null))
			{
				GameObject val3 = new GameObject("Bonecraft crack");
				val3.AddComponent<MeshFilter>().sharedMesh = val2;
				MeshRenderer val4 = val3.AddComponent<MeshRenderer>();
				((Renderer)val4).sharedMaterial = val;
				((Renderer)val4).shadowCastingMode = (ShadowCastingMode)0;
				active[cell] = (val3, val4, num, Time.unscaledTime);
			}
		}

		public void Hide(Cell cell)
		{
			if (active.Remove(cell, out (GameObject, MeshRenderer, int, float) value) && (Object)(object)value.Item1 != (Object)null)
			{
				Object.Destroy((Object)(object)value.Item1);
			}
		}

		public void Update()
		{
			List<Cell> list = null;
			foreach (var (item, tuple2) in active)
			{
				if (Time.unscaledTime - tuple2.Item4 > 5f)
				{
					(list ?? (list = new List<Cell>())).Add(item);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (Cell item2 in list)
			{
				Hide(item2);
			}
		}

		private static Mesh Overlay(in Box box, Vector3 point, Vector3 normal)
		{
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			normal = Vector3.Normalize(normal);
			Vector3 vector = Vector3.Abs(normal);
			int num = ((!(vector.X >= vector.Y) || !(vector.X >= vector.Z)) ? ((vector.Y >= vector.Z) ? 1 : 2) : 0);
			Vector3 vector2 = ((num == 1) ? Vector3.UnitX : Vector3.UnitY);
			Vector3 vector3 = Vector3.Normalize(Vector3.Cross(normal, vector2));
			Vector3 vector4 = Vector3.Cross(normal, vector3);
			float num2 = box.Size.Length() * 2f;
			List<Vector3> list = Clip.ClipToBox(new List<Vector3>
			{
				point + (vector3 + vector4) * num2,
				point + (vector3 - vector4) * num2,
				point + (-vector3 - vector4) * num2,
				point + (-vector3 + vector4) * num2
			}, in box);
			if (list.Count < 3)
			{
				return null;
			}
			float x = box.Size.X;
			Il2CppStructArray<Vector3> val = new Il2CppStructArray<Vector3>((long)list.Count);
			Il2CppStructArray<Vector2> val2 = new Il2CppStructArray<Vector2>((long)list.Count);
			Il2CppStructArray<Vector3> val3 = new Il2CppStructArray<Vector3>((long)list.Count);
			for (int i = 0; i < list.Count; i++)
			{
				Vector3 vector5 = list[i];
				Vector3 vector6 = (vector5 - box.Min) / x;
				Vector2 v = num switch
				{
					1 => new Vector2(vector6.X, vector6.Z), 
					0 => new Vector2(vector6.Z, vector6.Y), 
					_ => new Vector2(vector6.X, vector6.Y), 
				};
				((Il2CppArrayBase<Vector3>)(object)val)[i] = U.V3(vector5 + normal * 0.003f);
				((Il2CppArrayBase<Vector2>)(object)val2)[i] = U.V2(v);
				((Il2CppArrayBase<Vector3>)(object)val3)[i] = U.V3(normal);
			}
			List<int> list2 = new List<int>();
			bool flag = Vector3.Dot(Vector3.Cross(list[1] - list[0], list[2] - list[0]), normal) < 0f;
			for (int j = 1; j + 1 < list.Count; j++)
			{
				list2.AddRange(flag ? new int[3]
				{
					0,
					j + 1,
					j
				} : new int[3]
				{
					0,
					j,
					j + 1
				});
			}
			Mesh val4 = new Mesh
			{
				name = "Bonecraft crack",
				vertices = val,
				uv = val2,
				normals = val3,
				triangles = new Il2CppStructArray<int>(list2.ToArray())
			};
			val4.RecalculateBounds();
			return val4;
		}
	}
	internal sealed class Particles
	{
		private struct P
		{
			public Vector3 Pos;

			public Vector3 Prev;

			public Vector3 Vel;

			public int Age;

			public int Life;

			public float Half;

			public Vector2 Uv;

			public bool Ground;
		}

		private sealed class Group
		{
			public readonly List<P> List = new List<P>();

			public GameObject Go;

			public Mesh Mesh;
		}

		private const float Tick = 0.05f;

		private const int MaxPerGroup = 600;

		private readonly BlockLook look;

		private readonly Dictionary<string, Group> groups = new Dictionary<string, Group>();

		private readonly Random random = new Random();

		private float pending;

		private static float S => BonecraftMod.Settings.BlockSize.Value;

		public Particles(BlockLook look)
		{
			this.look = look;
		}

		public void Reset()
		{
			foreach (Group value in groups.Values)
			{
				if ((Object)(object)value.Go != (Object)null)
				{
					Object.Destroy((Object)(object)value.Go);
				}
			}
			groups.Clear();
		}

		private float R()
		{
			return (float)random.NextDouble();
		}

		private P Make(Vector3 pos, Vector3 dir)
		{
			Vector3 vector = new Vector3(dir.X + (R() * 2f - 1f) * 0.4f, dir.Y + (R() * 2f - 1f) * 0.4f, dir.Z + (R() * 2f - 1f) * 0.4f);
			float num = (R() + R() + 1f) * 0.15f;
			float num2 = Math.Max(1E-05f, vector.Length());
			vector = vector / num2 * num * 0.4f;
			vector.Y += 0.1f;
			return new P
			{
				Pos = pos,
				Prev = pos,
				Vel = vector,
				Life = (int)(4f / (R() * 0.9f + 0.1f)),
				Half = 0.1f * (R() * 0.5f + 0.5f),
				Uv = new Vector2(R() * 3f / 4f, R() * 3f / 4f)
			};
		}

		public void Destroy(string key, in Box box)
		{
			Group g = GroupFor(key);
			float x = box.Size.X;
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					for (int k = 0; k < 4; k++)
					{
						float num = ((float)i + 0.5f) / 4f;
						float num2 = ((float)j + 0.5f) / 4f;
						float num3 = ((float)k + 0.5f) / 4f;
						P p = Make(box.Min + new Vector3(num, num2, num3) * x, new Vector3(num - 0.5f, num2 - 0.5f, num3 - 0.5f));
						Add(g, p);
					}
				}
			}
		}

		public void Crack(string key, Vector3 point, Vector3 normal, int count)
		{
			Group g = GroupFor(key);
			Vector3 vector = Vector3.Normalize(normal);
			Vector3 vector2 = ((MathF.Abs(vector.Y) > 0.9f) ? Vector3.UnitX : Vector3.UnitY);
			Vector3 vector3 = Vector3.Normalize(Vector3.Cross(vector, vector2));
			Vector3 vector4 = Vector3.Cross(vector, vector3);
			for (int i = 0; i < count; i++)
			{
				Vector3 pos = point + (vector3 * (R() - 0.5f) + vector4 * (R() - 0.5f)) * 0.6f * S + vector * 0.1f * S;
				P p = Make(pos, Vector3.Zero);
				p.Vel = new Vector3(p.Vel.X * 0.2f, (p.Vel.Y - 0.1f) * 0.2f + 0.1f, p.Vel.Z * 0.2f);
				p.Half *= 0.6f;
				Add(g, p);
			}
		}

		private static void Add(Group g, P p)
		{
			if (g.List.Count < 600)
			{
				g.List.Add(p);
			}
		}

		private Group GroupFor(string key)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			if (!groups.TryGetValue(key, out var value) || (Object)(object)value.Go == (Object)null)
			{
				value = new Group
				{
					Go = new GameObject("Bonecraft particles " + key),
					Mesh = new Mesh
					{
						name = "Bonecraft particles"
					}
				};
				value.Mesh.MarkDynamic();
				value.Go.AddComponent<MeshFilter>().sharedMesh = value.Mesh;
				MeshRenderer obj = value.Go.AddComponent<MeshRenderer>();
				((Renderer)obj).sharedMaterial = look.Particle(key);
				((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
				groups[key] = value;
			}
			return value;
		}

		public void Update(float dt)
		{
			pending = Math.Min(pending + dt, 0.25f);
			while (pending >= 0.05f)
			{
				pending -= 0.05f;
				foreach (Group value in groups.Values)
				{
					for (int num = value.List.Count - 1; num >= 0; num--)
					{
						P p = value.List[num];
						if (p.Age++ >= p.Life)
						{
							value.List.RemoveAt(num);
						}
						else
						{
							p.Prev = p.Pos;
							p.Vel.Y -= 0.04f;
							Move(ref p);
							p.Vel *= 0.98f;
							if (p.Ground)
							{
								p.Vel.X *= 0.7f;
								p.Vel.Z *= 0.7f;
							}
							value.List[num] = p;
						}
					}
				}
			}
		}

		private static void Move(ref P p)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			Vector3 vector = p.Vel * S;
			float num = vector.Length();
			if (num < 1E-06f)
			{
				return;
			}
			Vector3 v = vector / num;
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(U.V3(p.Pos), U.V3(v), ref val, num + 0.01f, -1, (QueryTriggerInteraction)1) && (Object)(object)((RaycastHit)(ref val)).collider.attachedRigidbody == (Object)null)
			{
				Vector3 vector2 = U.N3(((RaycastHit)(ref val)).normal);
				p.Pos = U.N3(((RaycastHit)(ref val)).point) + vector2 * 0.01f;
				float num2 = Vector3.Dot(p.Vel, vector2);
				if (num2 < 0f)
				{
					p.Vel -= vector2 * num2;
				}
				p.Ground = vector2.Y > 0.5f;
			}
			else
			{
				p.Pos += vector;
				p.Ground = false;
			}
		}

		public void Render()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			Transform head = Player.Head;
			if ((Object)(object)head == (Object)null)
			{
				return;
			}
			Vector3 vector = U.N3(head.right);
			Vector3 vector2 = U.N3(head.up);
			Vector3 v = -U.N3(head.forward);
			float amount = pending / 0.05f;
			foreach (Group value in groups.Values)
			{
				if ((Object)(object)value.Go == (Object)null)
				{
					continue;
				}
				int count = value.List.Count;
				value.Mesh.Clear();
				if (count == 0)
				{
					continue;
				}
				Il2CppStructArray<Vector3> val = new Il2CppStructArray<Vector3>((long)(count * 4));
				Il2CppStructArray<Vector2> val2 = new Il2CppStructArray<Vector2>((long)(count * 4));
				Il2CppStructArray<Vector3> val3 = new Il2CppStructArray<Vector3>((long)(count * 4));
				int[] array = new int[count * 6];
				for (int i = 0; i < count; i++)
				{
					P p = value.List[i];
					Vector3 vector3 = Vector3.Lerp(p.Prev, p.Pos, amount);
					float num = p.Half * S;
					Vector3 vector4 = vector * num;
					Vector3 vector5 = vector2 * num;
					int num2 = i * 4;
					((Il2CppArrayBase<Vector3>)(object)val)[num2] = U.V3(vector3 - vector4 - vector5);
					((Il2CppArrayBase<Vector3>)(object)val)[num2 + 1] = U.V3(vector3 - vector4 + vector5);
					((Il2CppArrayBase<Vector3>)(object)val)[num2 + 2] = U.V3(vector3 + vector4 + vector5);
					((Il2CppArrayBase<Vector3>)(object)val)[num2 + 3] = U.V3(vector3 + vector4 - vector5);
					float x = p.Uv.X;
					float y = p.Uv.Y;
					float num3 = 0.25f;
					((Il2CppArrayBase<Vector2>)(object)val2)[num2] = U.V2(new Vector2(x, y));
					((Il2CppArrayBase<Vector2>)(object)val2)[num2 + 1] = U.V2(new Vector2(x, y + num3));
					((Il2CppArrayBase<Vector2>)(object)val2)[num2 + 2] = U.V2(new Vector2(x + num3, y + num3));
					((Il2CppArrayBase<Vector2>)(object)val2)[num2 + 3] = U.V2(new Vector2(x + num3, y));
					for (int j = 0; j < 4; j++)
					{
						((Il2CppArrayBase<Vector3>)(object)val3)[num2 + j] = U.V3(v);
					}
					int num4 = i * 6;
					array[num4] = num2;
					array[num4 + 1] = num2 + 1;
					array[num4 + 2] = num2 + 2;
					array[num4 + 3] = num2;
					array[num4 + 4] = num2 + 2;
					array[num4 + 5] = num2 + 3;
				}
				value.Mesh.vertices = val;
				value.Mesh.uv = val2;
				value.Mesh.normals = val3;
				value.Mesh.triangles = new Il2CppStructArray<int>(array);
				value.Mesh.RecalculateBounds();
			}
		}
	}
	internal static class GpuRead
	{
		public sealed class Job
		{
			public IntPtr Buffer;

			public byte[] Data;

			public string Error;

			public volatile bool Done;
		}

		private struct BufferDesc
		{
			public uint ByteWidth;

			public uint Usage;

			public uint BindFlags;

			public uint CpuAccessFlags;

			public uint MiscFlags;

			public uint StructureByteStride;
		}

		private struct Mapped
		{
			public IntPtr Data;

			public uint RowPitch;

			public uint DepthPitch;
		}

		private const int Release = 2;

		private const int GetDevice = 3;

		private const int BufferGetDesc = 10;

		private const int CreateBuffer = 3;

		private const int GetImmediateContext = 40;

		private const int Map = 14;

		private const int Unmap = 15;

		private const int CopyResource = 47;

		private const uint UsageStaging = 3u;

		private const uint CpuAccessRead = 131072u;

		private const uint MapRead = 1u;

		private static readonly ConcurrentQueue<Job> queue = new ConcurrentQueue<Job>();

		private static readonly Dictionary<IntPtr, (Job job, float at)> recent = new Dictionary<IntPtr, (Job, float)>();

		private unsafe static readonly IntPtr callback = (IntPtr)(delegate*<int, void>)(&OnRenderThread);

		private static bool issued;

		public static bool Supported => (int)SystemInfo.graphicsDeviceType == 2;

		public static Job Request(IntPtr d3dBuffer)
		{
			Job job = new Job
			{
				Buffer = d3dBuffer
			};
			if (d3dBuffer == IntPtr.Zero)
			{
				job.Error = "no GPU buffer";
				job.Done = true;
				return job;
			}
			if (recent.TryGetValue(d3dBuffer, out (Job, float) value) && Time.unscaledTime - value.Item2 < 5f && value.Item1.Error == null)
			{
				return value.Item1;
			}
			recent[d3dBuffer] = (job, Time.unscaledTime);
			queue.Enqueue(job);
			issued = false;
			return job;
		}

		public static void Pump()
		{
			if (!issued && !queue.IsEmpty)
			{
				issued = true;
				GL.IssuePluginEvent(callback, 0);
			}
		}

		[UnmanagedCallersOnly]
		private static void OnRenderThread(int eventId)
		{
			issued = false;
			Job result;
			while (queue.TryDequeue(out result))
			{
				try
				{
					result.Data = Copy(result.Buffer);
				}
				catch (Exception ex)
				{
					result.Error = ex.Message;
				}
				result.Done = true;
			}
		}

		private unsafe static void* Slot(IntPtr com, int index)
		{
			return *(void**)((nint)(*(IntPtr*)(void*)com) + (nint)index * (nint)sizeof(void*));
		}

		private unsafe static byte[] Copy(IntPtr buffer)
		{
			BufferDesc bufferDesc = default(BufferDesc);
			((delegate* unmanaged[Stdcall]<IntPtr, BufferDesc*, void>)Slot(buffer, 10))(buffer, &bufferDesc);
			IntPtr intPtr = default(IntPtr);
			((delegate* unmanaged[Stdcall]<IntPtr, IntPtr*, void>)Slot(buffer, 3))(buffer, &intPtr);
			IntPtr intPtr2 = default(IntPtr);
			((delegate* unmanaged[Stdcall]<IntPtr, IntPtr*, void>)Slot(intPtr, 40))(intPtr, &intPtr2);
			IntPtr zero = IntPtr.Zero;
			try
			{
				BufferDesc bufferDesc2 = new BufferDesc
				{
					ByteWidth = bufferDesc.ByteWidth,
					Usage = 3u,
					CpuAccessFlags = 131072u
				};
				int num = ((delegate* unmanaged[Stdcall]<IntPtr, BufferDesc*, void*, IntPtr*, int>)Slot(intPtr, 3))(intPtr, &bufferDesc2, null, &zero);
				if (num < 0)
				{
					throw new InvalidOperationException($"CreateBuffer failed (0x{num:x8})");
				}
				((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, IntPtr, void>)Slot(intPtr2, 47))(intPtr2, zero, buffer);
				Mapped mapped = default(Mapped);
				num = ((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, uint, uint, uint, Mapped*, int>)Slot(intPtr2, 14))(intPtr2, zero, 0u, 1u, 0u, &mapped);
				if (num < 0)
				{
					throw new InvalidOperationException($"Map failed (0x{num:x8})");
				}
				byte[] array = new byte[bufferDesc.ByteWidth];
				Marshal.Copy(mapped.Data, array, 0, array.Length);
				((delegate* unmanaged[Stdcall]<IntPtr, IntPtr, uint, void>)Slot(intPtr2, 15))(intPtr2, zero, 0u);
				return array;
			}
			finally
			{
				if (zero != IntPtr.Zero)
				{
					((delegate* unmanaged[Stdcall]<IntPtr, uint>)Slot(zero, 2))(zero);
				}
				((delegate* unmanaged[Stdcall]<IntPtr, uint>)Slot(intPtr2, 2))(intPtr2);
				((delegate* unmanaged[Stdcall]<IntPtr, uint>)Slot(intPtr, 2))(intPtr);
			}
		}
	}
	internal static class Json
	{
		public static string Obj(params object[] keyValues)
		{
			StringBuilder stringBuilder = new StringBuilder("{");
			for (int i = 0; i + 1 < keyValues.Length; i += 2)
			{
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				Str(stringBuilder, (string)keyValues[i]);
				stringBuilder.Append(':');
				Value(stringBuilder, keyValues[i + 1]);
			}
			return stringBuilder.Append('}').ToString();
		}

		private static void Value(StringBuilder sb, object v)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (v != null)
			{
				if (!(v is string s))
				{
					if (!(v is bool flag))
					{
						if (!(v is float f))
						{
							if (!(v is int[] values))
							{
								if (v is Vector3 val)
								{
									sb.Append('[');
									Num(sb, val.x);
									sb.Append(',');
									Num(sb, val.y);
									sb.Append(',');
									Num(sb, val.z);
									sb.Append(']');
								}
								else
								{
									sb.Append(Convert.ToString(v, CultureInfo.InvariantCulture));
								}
							}
							else
							{
								sb.Append('[').Append(string.Join(",", values)).Append(']');
							}
						}
						else
						{
							Num(sb, f);
						}
					}
					else
					{
						sb.Append(flag ? "true" : "false");
					}
				}
				else
				{
					Str(sb, s);
				}
			}
			else
			{
				sb.Append("null");
			}
		}

		private static void Num(StringBuilder sb, float f)
		{
			sb.Append(float.IsFinite(f) ? f.ToString("0.####", CultureInfo.InvariantCulture) : "0");
		}

		private static void Str(StringBuilder sb, string s)
		{
			sb.Append('"');
			foreach (char c in s)
			{
				if (c != '"')
				{
					if (c == '\\')
					{
						sb.Append("\\\\");
					}
					else if (c < ' ')
					{
						StringBuilder stringBuilder = sb.Append("\\u");
						int num = c;
						stringBuilder.Append(num.ToString("x4"));
					}
					else
					{
						sb.Append(c);
					}
				}
				else
				{
					sb.Append("\\\"");
				}
			}
			sb.Append('"');
		}
	}
	internal sealed class Link : IDisposable
	{
		private readonly Uri uri;

		private readonly Instance log;

		private readonly CancellationTokenSource stop = new CancellationTokenSource();

		private readonly BlockingCollection<string> outbox = new BlockingCollection<string>(new ConcurrentQueue<string>(), 256);

		private readonly ConcurrentQueue<string> inbox = new ConcurrentQueue<string>();

		private readonly ConcurrentQueue<Action> mainThread = new ConcurrentQueue<Action>();

		private volatile ClientWebSocket socket;

		public bool IsOpen
		{
			get
			{
				ClientWebSocket clientWebSocket = socket;
				if (clientWebSocket == null)
				{
					return false;
				}
				return clientWebSocket.State == WebSocketState.Open;
			}
		}

		public event Action Connected;

		public Link(string url, Instance log)
		{
			uri = new Uri(url);
			this.log = log;
		}

		public void Start()
		{
			Task.Run((Func<Task?>)Run);
		}

		public void Send(string json)
		{
			if (IsOpen)
			{
				outbox.TryAdd(json);
			}
		}

		public IEnumerable<string> Drain()
		{
			Action result;
			while (mainThread.TryDequeue(out result))
			{
				result();
			}
			string result2;
			while (inbox.TryDequeue(out result2))
			{
				yield return result2;
			}
		}

		private async Task Run()
		{
			bool announced = false;
			while (!stop.IsCancellationRequested)
			{
				using ClientWebSocket ws = new ClientWebSocket();
				try
				{
					await ws.ConnectAsync(uri, stop.Token);
					socket = ws;
					announced = false;
					log.Msg($"connected to Minecraft at {uri}");
					mainThread.Enqueue(delegate
					{
						this.Connected?.Invoke();
					});
					Task reader = Read(ws);
					while (ws.State == WebSocketState.Open && !reader.IsCompleted)
					{
						if (outbox.TryTake(out var item, 100, stop.Token))
						{
							await ws.SendAsync(Encoding.UTF8.GetBytes(item), WebSocketMessageType.Text, endOfMessage: true, stop.Token);
						}
					}
					log.Warning("lost the connection to Minecraft");
				}
				catch (OperationCanceledException)
				{
					break;
				}
				catch (Exception ex2)
				{
					if (!announced)
					{
						announced = true;
						log.Msg($"waiting for Minecraft at {uri} ({ex2.GetType().Name})");
					}
				}
				finally
				{
					socket = null;
					string item2;
					while (outbox.TryTake(out item2))
					{
					}
				}
				try
				{
					await Task.Delay(2000, stop.Token);
				}
				catch (OperationCanceledException)
				{
					break;
				}
			}
		}

		private async Task Read(ClientWebSocket ws)
		{
			byte[] buffer = new byte[16384];
			StringBuilder text = new StringBuilder();
			try
			{
				while (ws.State == WebSocketState.Open)
				{
					WebSocketReceiveResult webSocketReceiveResult = await ws.ReceiveAsync(buffer, stop.Token);
					if (webSocketReceiveResult.MessageType == WebSocketMessageType.Close)
					{
						break;
					}
					text.Append(Encoding.UTF8.GetString(buffer, 0, webSocketReceiveResult.Count));
					if (webSocketReceiveResult.EndOfMessage)
					{
						inbox.Enqueue(text.ToString());
						text.Clear();
					}
				}
			}
			catch (Exception)
			{
			}
		}

		public void Dispose()
		{
			stop.Cancel();
		}
	}
	internal static class Materials
	{
		private static readonly (string surface, string block)[] Surfaces = new(string, string)[21]
		{
			("redbrick", "minecraft:bricks"),
			("red brick", "minecraft:bricks"),
			("concrete", "minecraft:light_gray_concrete"),
			("crystalline", "minecraft:amethyst_block"),
			("glass", "minecraft:glass"),
			("pure metal", "minecraft:iron_block"),
			("metal", "minecraft:iron_block"),
			("hydraulic", "minecraft:iron_block"),
			("wood", "minecraft:oak_planks"),
			("dirt", "minecraft:dirt"),
			("plant", "minecraft:moss_block"),
			("cloth", "minecraft:white_wool"),
			("cardboard", "minecraft:brown_wool"),
			("paper", "minecraft:white_wool"),
			("polymer", "minecraft:white_concrete"),
			("bioluminescent", "minecraft:shroomlight"),
			("void", "minecraft:black_concrete"),
			("bone", "minecraft:bone_block"),
			("blood", "minecraft:redstone_block"),
			("melon", "minecraft:melon"),
			("zapper", "minecraft:redstone_block")
		};

		private static readonly (string word, string block)[] Refinements = new(string, string)[27]
		{
			("grass", "minecraft:grass_block"),
			("lawn", "minecraft:grass_block"),
			("sandstone", "minecraft:sandstone"),
			("sand", "minecraft:sand"),
			("gravel", "minecraft:gravel"),
			("snow", "minecraft:snow_block"),
			("mud", "minecraft:mud"),
			("brick", "minecraft:bricks"),
			("cobble", "minecraft:cobblestone"),
			("marble", "minecraft:quartz_block"),
			("tile", "minecraft:smooth_quartz"),
			("asphalt", "minecraft:gray_concrete"),
			("road", "minecraft:gray_concrete"),
			("rock", "minecraft:stone"),
			("cliff", "minecraft:stone"),
			("stone", "minecraft:stone"),
			("plaster", "minecraft:white_concrete"),
			("drywall", "minecraft:white_concrete"),
			("rust", "minecraft:oxidized_copper"),
			("copper", "minecraft:copper_block"),
			("gold", "minecraft:gold_block"),
			("bark", "minecraft:oak_log"),
			("trunk", "minecraft:oak_log"),
			("log", "minecraft:oak_log"),
			("darkwood", "minecraft:dark_oak_planks"),
			("carpet", "minecraft:red_wool"),
			("lava", "minecraft:magma_block")
		};

		private static bool Refines(string surfaceBlock, string refined)
		{
			switch (surfaceBlock)
			{
			case "minecraft:dirt":
			{
				bool result;
				switch (refined)
				{
				case "minecraft:grass_block":
				case "minecraft:sand":
				case "minecraft:gravel":
				case "minecraft:snow_block":
				case "minecraft:mud":
				case "minecraft:stone":
					result = true;
					break;
				default:
					result = false;
					break;
				}
				return result;
			}
			case "minecraft:light_gray_concrete":
			{
				bool result;
				switch (refined)
				{
				case "minecraft:gray_concrete":
				case "minecraft:smooth_quartz":
				case "minecraft:bricks":
				case "minecraft:cobblestone":
				case "minecraft:quartz_block":
				case "minecraft:stone":
				case "minecraft:white_concrete":
				case "minecraft:sandstone":
				case "minecraft:snow_block":
					result = true;
					break;
				default:
					result = false;
					break;
				}
				return result;
			}
			case "minecraft:iron_block":
			{
				bool result;
				switch (refined)
				{
				case "minecraft:oxidized_copper":
				case "minecraft:copper_block":
				case "minecraft:gold_block":
					result = true;
					break;
				default:
					result = false;
					break;
				}
				return result;
			}
			case "minecraft:oak_planks":
				return (refined == "minecraft:oak_log" || refined == "minecraft:dark_oak_planks") ? true : false;
			case "minecraft:white_wool":
				return refined == "minecraft:red_wool";
			default:
				return false;
			}
		}

		public static string BlockFor(Collider collider, Renderer surface)
		{
			string text = Match(Names(collider, surface), Refinements);
			string text2 = SurfaceName(collider);
			if (text2 != null)
			{
				string text3 = Match(text2, Surfaces);
				if (text3 != null)
				{
					if (text == null || !Refines(text3, text))
					{
						return text3;
					}
					return text;
				}
			}
			return text ?? BlockLook.Guess(collider, surface);
		}

		public static string SurfaceName(Collider c)
		{
			try
			{
				ImpactProperties val = ((Component)c).GetComponent<ImpactProperties>();
				if ((Object)(object)val == (Object)null && (Object)(object)c.attachedRigidbody == (Object)null)
				{
					val = ((Component)c).GetComponentInParent<ImpactProperties>();
				}
				SurfaceData val2 = (((Object)(object)val == (Object)null) ? null : (val.surfaceData ?? val._cachedSurfaceData));
				return ((Object)(object)val2 == (Object)null) ? null : ((Object)val2).name.ToLowerInvariant();
			}
			catch
			{
				return null;
			}
		}

		private static string Names(Collider c, Renderer r)
		{
			string text = ((Object)((Component)c).gameObject).name + " " + (((Object)(object)c.sharedMaterial != (Object)null) ? ((Object)c.sharedMaterial).name : "");
			if ((Object)(object)r != (Object)null)
			{
				text = text + " " + ((Object)((Component)r).gameObject).name;
				foreach (Material item in (Il2CppArrayBase<Material>)(object)r.sharedMaterials)
				{
					if ((Object)(object)item != (Object)null)
					{
						text = text + " " + ((Object)item).name;
					}
				}
			}
			return text.ToLowerInvariant();
		}

		private static string Match(stri