Decompiled source of bwpaint v1.0.0

bwpaint.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Microsoft.CodeAnalysis;
using Mirror;
using Rewired;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("bwpaint")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("bwpaint")]
[assembly: AssemblyTitle("bwpaint")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace bwpaint
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "bwpaint";

		public const string PLUGIN_NAME = "bwpaint";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace BigWalkWhiteboard
{
	internal sealed class BoardSession
	{
		private int rPrevX;

		private int rPrevY;

		private bool rHasPrev;

		private bool rStrokeOpen;

		public DrawingSurface Surface { get; private set; }

		public WhiteboardBoard Board { get; private set; }

		public void Ensure()
		{
			if (Surface != null && !Surface.IsAlive)
			{
				Surface = null;
				Board = null;
			}
			if (Surface == null)
			{
				Surface = new DrawingSurface();
			}
			if (Board == null)
			{
				Board = new WhiteboardBoard(Surface);
			}
			Board.EnsureBuilt();
		}

		public void EndRemoteStroke()
		{
			if (rStrokeOpen)
			{
				if (Surface != null)
				{
					Surface.EndStroke();
				}
				rStrokeOpen = false;
				rHasPrev = false;
			}
		}

		public void ApplyPoints(byte flags, int radius, Color32 c, short[] xs, short[] ys, int count)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			DrawingSurface surface = Surface;
			if ((flags & 1) != 0 || !rStrokeOpen)
			{
				if (rStrokeOpen)
				{
					surface.EndStroke();
				}
				surface.BeginStroke();
				rStrokeOpen = true;
				rHasPrev = false;
			}
			if ((flags & 4) != 0)
			{
				rHasPrev = false;
			}
			for (int i = 0; i < count; i++)
			{
				int num = xs[i];
				int num2 = ys[i];
				if (rHasPrev)
				{
					ShapeDrawing.Line(surface, rPrevX, rPrevY, num, num2, radius, c);
				}
				else
				{
					surface.Stamp(num, num2, radius, c);
				}
				rPrevX = num;
				rPrevY = num2;
				rHasPrev = true;
			}
			if ((flags & 2) != 0)
			{
				EndRemoteStroke();
			}
		}

		public void ApplyShape(byte tool, int radius, Color32 c, bool filled, int x0, int y0, int x1, int y1)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			EndRemoteStroke();
			DrawingSurface surface = Surface;
			surface.BeginStroke();
			switch ((Tool)tool)
			{
			case Tool.Line:
				ShapeDrawing.Line(surface, x0, y0, x1, y1, radius, c);
				break;
			case Tool.Rect:
				ShapeDrawing.Rect(surface, x0, y0, x1, y1, radius, c, filled);
				break;
			case Tool.Ellipse:
				ShapeDrawing.Ellipse(surface, x0, y0, x1, y1, radius, c, filled);
				break;
			}
			surface.EndStroke();
		}

		public void ApplyFill(int x, int y, Color32 c)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			EndRemoteStroke();
			Surface.BeginStroke();
			FloodFill.Fill(Surface, x, y, c);
			Surface.EndStroke();
		}

		public void ApplyUndo()
		{
			Ensure();
			EndRemoteStroke();
			Surface.Undo();
		}

		public void ApplyWipe()
		{
			Ensure();
			EndRemoteStroke();
			Surface.Wipe();
		}

		public void ApplyPlace(bool active, Vector3 pos, float yaw, float scale)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			Board.SetPlacement(pos, yaw, scale);
			Board.SetActive(active);
		}

		public void ApplySnapshot(byte[] data, int len)
		{
			Ensure();
			EndRemoteStroke();
			SnapshotCodec.Decode(Surface, data, len);
		}
	}
	internal sealed class DrawingSurface
	{
		private sealed class Stroke
		{
			public int[] Idx;

			public uint[] Old;

			public int Count;

			public void Add(int index, uint oldColor)
			{
				if (Idx == null)
				{
					Idx = new int[1024];
					Old = new uint[1024];
				}
				else if (Count == Idx.Length)
				{
					Array.Resize(ref Idx, Count * 2);
					Array.Resize(ref Old, Count * 2);
				}
				Idx[Count] = index;
				Old[Count] = oldColor;
				Count++;
			}
		}

		public const int Width = 1280;

		public const int Height = 640;

		private const int MaxUndo = 50;

		public readonly Texture2D Tex;

		private readonly Il2CppStructArray<Color32> pixels;

		private readonly int[] mark = new int[819200];

		private readonly List<Stroke> history = new List<Stroke>(51);

		private Stroke current;

		private int strokeId;

		private bool dirty;

		public bool IsAlive => (Object)(object)Tex != (Object)null;

		public int UndoCount => history.Count;

		public DrawingSurface()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Tex = new Texture2D(1280, 640, (TextureFormat)4, false);
			((Texture)Tex).wrapMode = (TextureWrapMode)1;
			((Texture)Tex).filterMode = (FilterMode)1;
			pixels = new Il2CppStructArray<Color32>(819200L);
			FillRaw(Palette.Paper);
			dirty = true;
			Flush();
		}

		public void BeginStroke()
		{
			strokeId++;
			current = new Stroke();
		}

		public void EndStroke()
		{
			if (current == null)
			{
				return;
			}
			if (current.Count > 0)
			{
				history.Add(current);
				if (history.Count > 50)
				{
					history.RemoveAt(0);
				}
			}
			current = null;
		}

		public void RevertStroke()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (current != null && current.Count != 0)
			{
				for (int num = current.Count - 1; num >= 0; num--)
				{
					((Il2CppArrayBase<Color32>)(object)pixels)[current.Idx[num]] = Unpack(current.Old[num]);
				}
				current.Count = 0;
				strokeId++;
				dirty = true;
			}
		}

		public Color32 GetPixel(int x, int y)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (x < 0)
			{
				x = 0;
			}
			else if (x >= 1280)
			{
				x = 1279;
			}
			if (y < 0)
			{
				y = 0;
			}
			else if (y >= 640)
			{
				y = 639;
			}
			return ((Il2CppArrayBase<Color32>)(object)pixels)[y * 1280 + x];
		}

		public void SetPixel(int x, int y, Color32 c)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if ((uint)x < 1280u && (uint)y < 640u)
			{
				Put(y * 1280 + x, c);
			}
		}

		public void FillSpan(int y, int x0, int x1, Color32 c)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((uint)y < 640u)
			{
				if (x0 > x1)
				{
					int num = x0;
					x0 = x1;
					x1 = num;
				}
				if (x0 < 0)
				{
					x0 = 0;
				}
				if (x1 > 1279)
				{
					x1 = 1279;
				}
				int num2 = y * 1280;
				for (int i = x0; i <= x1; i++)
				{
					Put(num2 + i, c);
				}
			}
		}

		public void Stamp(int cx, int cy, int r, Color32 c)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Max(0, cx - r);
			int num2 = Math.Min(1279, cx + r);
			int num3 = Math.Max(0, cy - r);
			int num4 = Math.Min(639, cy + r);
			int num5 = r * r;
			for (int i = num3; i <= num4; i++)
			{
				int num6 = i - cy;
				int num7 = i * 1280;
				for (int j = num; j <= num2; j++)
				{
					int num8 = j - cx;
					if (num8 * num8 + num6 * num6 <= num5)
					{
						Put(num7 + j, c);
					}
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void Put(int i, Color32 c)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_009b: 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)
			Color32 val = ((Il2CppArrayBase<Color32>)(object)pixels)[i];
			if (val.r != c.r || val.g != c.g || val.b != c.b || val.a != c.a)
			{
				if (current != null && mark[i] != strokeId)
				{
					mark[i] = strokeId;
					current.Add(i, Pack(val));
				}
				((Il2CppArrayBase<Color32>)(object)pixels)[i] = c;
				dirty = true;
			}
		}

		public bool Undo()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (current != null || history.Count == 0)
			{
				return false;
			}
			Stroke stroke = history[history.Count - 1];
			history.RemoveAt(history.Count - 1);
			for (int num = stroke.Count - 1; num >= 0; num--)
			{
				((Il2CppArrayBase<Color32>)(object)pixels)[stroke.Idx[num]] = Unpack(stroke.Old[num]);
			}
			dirty = true;
			return true;
		}

		public void Wipe()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			current = null;
			history.Clear();
			FillRaw(Palette.Paper);
			dirty = true;
		}

		public void BeginRawLoad()
		{
			current = null;
			history.Clear();
		}

		public void RawFillSpan(int y, int x0, int x1, Color32 c)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if ((uint)y < 640u)
			{
				if (x0 < 0)
				{
					x0 = 0;
				}
				if (x1 > 1279)
				{
					x1 = 1279;
				}
				int num = y * 1280;
				for (int i = x0; i <= x1; i++)
				{
					((Il2CppArrayBase<Color32>)(object)pixels)[num + i] = c;
				}
				dirty = true;
			}
		}

		private void FillRaw(Color32 c)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			int num = 819200;
			for (int i = 0; i < num; i++)
			{
				((Il2CppArrayBase<Color32>)(object)pixels)[i] = c;
			}
		}

		public void Flush()
		{
			if (dirty)
			{
				Tex.SetPixels32(pixels);
				Tex.Apply(false);
				dirty = false;
			}
		}

		public void CopyRowRgb(int y, byte[] dest, int offset)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			int num = y * 1280;
			for (int i = 0; i < 1280; i++)
			{
				Color32 val = ((Il2CppArrayBase<Color32>)(object)pixels)[num + i];
				dest[offset++] = val.r;
				dest[offset++] = val.g;
				dest[offset++] = val.b;
			}
		}

		public string SavePng()
		{
			string text = Path.Combine(Paths.BepInExRootPath, "Whiteboards");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, "whiteboard_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".png");
			File.WriteAllBytes(text2, PngWriter.Encode(this));
			return text2;
		}

		private static uint Pack(Color32 c)
		{
			//IL_0000: 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_000f: 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)
			return (uint)(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24));
		}

		private static Color32 Unpack(uint v)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return new Color32((byte)v, (byte)(v >> 8), (byte)(v >> 16), (byte)(v >> 24));
		}
	}
	internal static class FloodFill
	{
		private static int[] stack = new int[4096];

		private static int sp;

		public static void Fill(DrawingSurface s, int sx, int sy, Color32 fill)
		{
			//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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//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_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			if ((uint)sx >= 1280u || (uint)sy >= 640u)
			{
				return;
			}
			Color32 pixel = s.GetPixel(sx, sy);
			if (Same(pixel, fill))
			{
				return;
			}
			sp = 0;
			Push(sy * 1280 + sx);
			while (sp > 0)
			{
				int num = stack[--sp];
				int num2 = num / 1280;
				int i = num - num2 * 1280;
				while (i > 0 && Same(s.GetPixel(i - 1, num2), pixel))
				{
					i--;
				}
				bool flag = false;
				bool flag2 = false;
				for (; i < 1280 && Same(s.GetPixel(i, num2), pixel); i++)
				{
					s.SetPixel(i, num2, fill);
					if (num2 > 0)
					{
						bool flag3 = Same(s.GetPixel(i, num2 - 1), pixel);
						if (flag3 && !flag)
						{
							Push((num2 - 1) * 1280 + i);
						}
						flag = flag3;
					}
					if (num2 < 639)
					{
						bool flag4 = Same(s.GetPixel(i, num2 + 1), pixel);
						if (flag4 && !flag2)
						{
							Push((num2 + 1) * 1280 + i);
						}
						flag2 = flag4;
					}
				}
			}
		}

		private static void Push(int v)
		{
			if (sp == stack.Length)
			{
				Array.Resize(ref stack, sp * 2);
			}
			stack[sp++] = v;
		}

		private static bool Same(Color32 a, Color32 b)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
		}
	}
	internal sealed class HandSwatch
	{
		private const bool CameraOnly = false;

		private const int MaxHandSearches = 3;

		private const float RetrySeconds = 1.5f;

		private static readonly Vector3 Size = new Vector3(0.1f, 0.14f, 0.01f);

		private static readonly Vector3 CameraOffset = new Vector3(0.22f, -0.18f, 0.5f);

		private static readonly string[] HandKeywords = new string[5] { "hand", "wing", "arm", "paw", "claw" };

		private GameObject go;

		private Material mat;

		private Transform parent;

		private bool onCamera;

		private float nextAttempt;

		private int searches;

		private bool dumped;

		private Color32 color = Palette.Colors[0];

		public void SetColor(Color32 c)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			color = c;
			if ((Object)(object)mat != (Object)null)
			{
				MaterialUtil.SetColor(mat, c);
			}
		}

		public void Tick(Camera cam, float now)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)go == (Object)null && !Build())
			{
				return;
			}
			if (!go.activeSelf)
			{
				go.SetActive(true);
			}
			bool flag = searches < 3;
			if ((!((Object)(object)parent == (Object)null) && !(onCamera && flag)) || now < nextAttempt)
			{
				return;
			}
			nextAttempt = now + 1.5f;
			if (flag)
			{
				searches++;
				Transform val = FindHand();
				if ((Object)(object)val != (Object)null)
				{
					Attach(val, Vector3.zero, camera: false);
					return;
				}
			}
			if ((Object)(object)parent == (Object)null && (Object)(object)cam != (Object)null)
			{
				Attach(((Component)cam).transform, CameraOffset, camera: true);
			}
		}

		public void Hide()
		{
			searches = 0;
			if ((Object)(object)go != (Object)null && go.activeSelf)
			{
				go.SetActive(false);
			}
		}

		private bool Build()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			go = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)go).name = "BW_Swatch";
			Collider component = go.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			mat = MaterialUtil.Make(color, null);
			if ((Object)(object)mat != (Object)null)
			{
				go.GetComponent<Renderer>().material = mat;
			}
			parent = null;
			return true;
		}

		private void Attach(Transform t, Vector3 offset, bool camera)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = go.transform;
			transform.SetParent(t, false);
			Vector3 lossyScale = t.lossyScale;
			transform.localScale = new Vector3(Size.x / Nz(lossyScale.x), Size.y / Nz(lossyScale.y), Size.z / Nz(lossyScale.z));
			transform.localPosition = offset;
			transform.localRotation = Quaternion.identity;
			parent = t;
			onCamera = camera;
		}

		private static float Nz(float v)
		{
			return (Mathf.Abs(v) < 0.0001f) ? 1f : v;
		}

		private Transform FindHand()
		{
			NetworkIdentity localPlayer = NetworkClient.localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			Il2CppArrayBase<Transform> componentsInChildren = ((Component)localPlayer).GetComponentsInChildren<Transform>(true);
			Transform val = null;
			int num = 0;
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Transform val2 = componentsInChildren[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string text = ((Object)val2).name.ToLowerInvariant();
				int num2 = 0;
				for (int j = 0; j < HandKeywords.Length; j++)
				{
					if (text.Contains(HandKeywords[j]))
					{
						num2 = (HandKeywords.Length - j) * 10;
						break;
					}
				}
				if (num2 != 0)
				{
					if (text.Contains("right") || text.EndsWith("_r") || text.EndsWith(".r") || text.EndsWith(" r"))
					{
						num2 += 5;
					}
					if (num2 > num)
					{
						num = num2;
						val = val2;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				Plugin.Log.LogInfo((object)("Whiteboard: swatch attached to '" + ((Object)val).name + "'"));
			}
			else if (!dumped)
			{
				dumped = true;
				Plugin.Log.LogInfo((object)"Whiteboard: no hand-like bone found; using camera fallback. Player transforms:");
				int num3 = ((componentsInChildren.Length < 80) ? componentsInChildren.Length : 80);
				for (int k = 0; k < num3; k++)
				{
					if ((Object)(object)componentsInChildren[k] != (Object)null)
					{
						Plugin.Log.LogInfo((object)("  " + ((Object)componentsInChildren[k]).name));
					}
				}
			}
			return val;
		}
	}
	internal enum PadMode
	{
		None,
		Hint,
		Full
	}
	internal sealed class InfoPad
	{
		private const float PadW = 840f;

		private const float PadH = 84f;

		private const float MessageSeconds = 3f;

		private const string Help = "C/V color   [ ] size   F filled shapes   +/- board size   U undo   Del wipe   F11 save   E exit";

		private static readonly Color Bg = new Color(0f, 0f, 0f, 0.72f);

		private static readonly Color Border = new Color(1f, 1f, 1f, 0.9f);

		private GUIStyle label;

		private GUIStyle helpStyle;

		private GUIStyle msgStyle;

		private GUIStyle hintStyle;

		private Tool tool = Tool.Pencil;

		private int colorIdx = int.MinValue;

		private Color32 color;

		private int brush = -1;

		private bool filled;

		private float scale = -1f;

		private int undo = -1;

		private bool dirty = true;

		private string toolsLine = "";

		private string statusLine = "";

		private string message = "";

		private float messageUntil;

		private readonly StringBuilder sb = new StringBuilder(256);

		public void SetState(Tool t, int idx, Color32 c, int brushSize, bool filledShapes, float boardScale, int undoCount)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (t != tool || idx != colorIdx || c.r != color.r || c.g != color.g || c.b != color.b || brushSize != brush || filledShapes != filled || !Mathf.Approximately(boardScale, scale) || undoCount != undo)
			{
				tool = t;
				colorIdx = idx;
				color = c;
				brush = brushSize;
				filled = filledShapes;
				scale = boardScale;
				undo = undoCount;
				dirty = true;
			}
		}

		public void Say(string text, float now)
		{
			message = text;
			messageUntil = now + 3f;
		}

		public void Draw(PadMode mode, float now, string hint)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_0065: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: 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_01f8: Unknown result type (might be due to invalid IL or missing references)
			if (mode == PadMode.None || (int)Event.current.type != 7)
			{
				return;
			}
			EnsureStyles();
			if (mode == PadMode.Hint)
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((float)Screen.width - 340f) * 0.5f, 8f, 340f, 30f);
				Fill(val, Bg);
				GUI.Label(val, hint, hintStyle);
				return;
			}
			if (dirty)
			{
				Rebuild();
			}
			float num = Mathf.Min(840f, (float)Screen.width - 20f);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((float)Screen.width - num) * 0.5f, 8f, num, 84f);
			Fill(r, Bg);
			Rect r2 = default(Rect);
			((Rect)(ref r2))..ctor(((Rect)(ref r)).x + 14f, ((Rect)(ref r)).y + 12f, 60f, 60f);
			Fill(new Rect(((Rect)(ref r2)).x - 2f, ((Rect)(ref r2)).y - 2f, ((Rect)(ref r2)).width + 4f, ((Rect)(ref r2)).height + 4f), Border);
			Fill(r2, Color32.op_Implicit(color));
			float num2 = ((Rect)(ref r2)).xMax + 16f;
			float num3 = ((Rect)(ref r)).xMax - num2 - 8f;
			GUI.Label(new Rect(num2, ((Rect)(ref r)).y + 6f, num3, 22f), toolsLine, label);
			GUI.Label(new Rect(num2, ((Rect)(ref r)).y + 29f, num3, 22f), statusLine, label);
			if (now < messageUntil)
			{
				GUI.Label(new Rect(num2, ((Rect)(ref r)).y + 54f, num3, 22f), message, msgStyle);
			}
			else
			{
				GUI.Label(new Rect(num2, ((Rect)(ref r)).y + 54f, num3, 22f), "C/V color   [ ] size   F filled shapes   +/- board size   U undo   Del wipe   F11 save   E exit", helpStyle);
			}
		}

		private void Rebuild()
		{
			sb.Length = 0;
			for (int i = 0; i < ToolInfo.Count; i++)
			{
				if (i > 0)
				{
					sb.Append("    ");
				}
				bool flag = i == (int)tool;
				if (flag)
				{
					sb.Append("<color=#FFD54A><b>");
				}
				sb.Append(i + 1).Append(' ').Append(ToolInfo.Names[i]);
				if (flag)
				{
					sb.Append("</b></color>");
				}
			}
			toolsLine = sb.ToString();
			sb.Length = 0;
			sb.Append("Color: ");
			if (colorIdx >= 0 && colorIdx < Palette.Count)
			{
				sb.Append(Palette.Names[colorIdx]);
			}
			else
			{
				sb.Append("Custom #").Append(color.r.ToString("X2")).Append(color.g.ToString("X2"))
					.Append(color.b.ToString("X2"));
			}
			sb.Append("     Size: ").Append(brush).Append("     Shapes: ")
				.Append(filled ? "Filled" : "Outline")
				.Append("     Board: ")
				.Append(scale.ToString("0.00"))
				.Append('x')
				.Append("     Undo: ")
				.Append(undo);
			statusLine = sb.ToString();
			dirty = false;
		}

		private void EnsureStyles()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			if (label == null)
			{
				label = new GUIStyle(GUI.skin.label);
				label.fontSize = 15;
				label.richText = true;
				label.wordWrap = false;
				label.normal.textColor = Color.white;
				helpStyle = new GUIStyle(label);
				helpStyle.fontSize = 13;
				helpStyle.normal.textColor = new Color(0.75f, 0.75f, 0.75f, 1f);
				msgStyle = new GUIStyle(label);
				msgStyle.fontSize = 14;
				msgStyle.fontStyle = (FontStyle)1;
				msgStyle.normal.textColor = new Color(1f, 0.84f, 0.29f, 1f);
				hintStyle = new GUIStyle(label);
				hintStyle.alignment = (TextAnchor)4;
			}
		}

		private static void Fill(Rect r, Color c)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			GUI.color = c;
			GUI.DrawTexture(r, (Texture)(object)Texture2D.whiteTexture);
			GUI.color = Color.white;
		}
	}
	internal static class RewiredLock
	{
		private static bool locked;

		private static bool broken;

		public static void Set(bool lockInput)
		{
			if (broken || locked == lockInput)
			{
				return;
			}
			try
			{
				int playerCount = ReInput.players.playerCount;
				for (int i = 0; i < playerCount; i++)
				{
					Player player = ReInput.players.GetPlayer(i);
					if (player != null)
					{
						player.controllers.maps.SetAllMapsEnabled(!lockInput);
					}
				}
				locked = lockInput;
			}
			catch (Exception ex)
			{
				broken = true;
				Plugin.Log.LogWarning((object)("Whiteboard: Rewired input lock failed (camera will not freeze): " + ex.Message));
			}
		}
	}
	[HarmonyPatch]
	internal static class CursorBlock
	{
		internal static bool Active;

		internal static bool Bypass;

		private static bool Blocking => Active && !Bypass;

		internal static void SetCursor(bool free)
		{
			Bypass = true;
			try
			{
				Cursor.lockState = (CursorLockMode)(!free);
				Cursor.visible = free;
			}
			finally
			{
				Bypass = false;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static bool LockState(CursorLockMode value)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			return !Blocking || (int)value == 0;
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static bool Visible(bool value)
		{
			return !Blocking || value;
		}
	}
	internal static class MaterialUtil
	{
		private static readonly string[] Candidates = new string[6] { "Universal Render Pipeline/Unlit", "Unlit/Texture", "HDRP/Unlit", "Sprites/Default", "Universal Render Pipeline/Lit", "Standard" };

		private static Shader shader;

		public static Material Make(Color32 color, Texture texture)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shader == (Object)null)
			{
				for (int i = 0; i < Candidates.Length; i++)
				{
					shader = Shader.Find(Candidates[i]);
					if ((Object)(object)shader != (Object)null)
					{
						break;
					}
				}
				if ((Object)(object)shader == (Object)null)
				{
					Plugin.Log.LogError((object)"Whiteboard: no usable shader found");
					return null;
				}
			}
			Material val = new Material(shader);
			SetColor(val, color);
			if ((Object)(object)texture != (Object)null)
			{
				if (val.HasProperty("_BaseMap"))
				{
					val.SetTexture("_BaseMap", texture);
				}
				if (val.HasProperty("_MainTex"))
				{
					val.SetTexture("_MainTex", texture);
				}
			}
			return val;
		}

		public static void SetColor(Material m, Color32 color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Color val = Color32.op_Implicit(color);
			if (m.HasProperty("_BaseColor"))
			{
				m.SetColor("_BaseColor", val);
			}
			if (m.HasProperty("_Color"))
			{
				m.SetColor("_Color", val);
			}
		}
	}
	internal static class MirrorTransport
	{
		public const ushort MsgId = 46973;

		private const int Reliable = 0;

		public static Action<NetworkConnection, byte[], int> ServerReceived;

		public static Action<byte[], int> ClientReceived;

		private static NetworkMessageDelegate serverDel;

		private static NetworkMessageDelegate clientDel;

		private static int errs;

		public static bool IsHost => NetworkServer.active;

		public static bool ClientConnected => !NetworkServer.active && NetworkClient.isConnected;

		public static bool Networked => NetworkServer.active || NetworkClient.active;

		public static bool ConnectionAlive(int id)
		{
			try
			{
				return NetworkServer.connections.ContainsKey(id);
			}
			catch
			{
				return false;
			}
		}

		public static void EnsureHandlers()
		{
			try
			{
				if ((Delegate)(object)serverDel == (Delegate)null)
				{
					serverDel = DelegateSupport.ConvertDelegate<NetworkMessageDelegate>((Delegate)new Action<NetworkConnection, NetworkReader, int>(OnServer));
				}
				if ((Delegate)(object)clientDel == (Delegate)null)
				{
					clientDel = DelegateSupport.ConvertDelegate<NetworkMessageDelegate>((Delegate)new Action<NetworkConnection, NetworkReader, int>(OnClient));
				}
				if (NetworkServer.active && !NetworkServer.handlers.ContainsKey((ushort)46973))
				{
					NetworkServer.handlers[(ushort)46973] = serverDel;
				}
				if (NetworkClient.active && !NetworkClient.handlers.ContainsKey((ushort)46973))
				{
					NetworkClient.handlers[(ushort)46973] = clientDel;
				}
			}
			catch (Exception e)
			{
				Fail("EnsureHandlers", e);
			}
		}

		private static void OnServer(NetworkConnection conn, NetworkReader reader, int channelId)
		{
			try
			{
				Action<NetworkConnection, byte[], int> serverReceived = ServerReceived;
				if (serverReceived != null)
				{
					int n;
					byte[] arg = ReadAll(reader, out n);
					serverReceived(conn, arg, n);
				}
			}
			catch (Exception e)
			{
				Fail("OnServer", e);
			}
		}

		private static void OnClient(NetworkConnection conn, NetworkReader reader, int channelId)
		{
			try
			{
				Action<byte[], int> clientReceived = ClientReceived;
				if (clientReceived != null)
				{
					int n;
					byte[] arg = ReadAll(reader, out n);
					clientReceived(arg, n);
				}
			}
			catch (Exception e)
			{
				Fail("OnClient", e);
			}
		}

		private static byte[] ReadAll(NetworkReader reader, out int n)
		{
			n = reader.Remaining;
			byte[] array = new byte[n];
			for (int i = 0; i < n; i++)
			{
				array[i] = reader.ReadByte();
			}
			return array;
		}

		private static NetworkWriter BuildWriter(byte[] data, int len)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			NetworkWriter val = new NetworkWriter();
			val.WriteByte((byte)125);
			val.WriteByte((byte)183);
			Il2CppStructArray<byte> val2 = Il2CppStructArray<byte>.op_Implicit(data);
			val.WriteBytes(val2, 0, len);
			return val;
		}

		public static bool SendToServer(byte[] data, int len)
		{
			try
			{
				NetworkConnection connection = NetworkClient.connection;
				if (connection == null)
				{
					return false;
				}
				NetworkWriter val = BuildWriter(data, len);
				connection.Send(val.ToArraySegment(), 0);
				return true;
			}
			catch (Exception e)
			{
				Fail("SendToServer", e);
				return false;
			}
		}

		public static void Send(NetworkConnection conn, byte[] data, int len)
		{
			try
			{
				NetworkWriter val = BuildWriter(data, len);
				conn.Send(val.ToArraySegment(), 0);
			}
			catch (Exception e)
			{
				Fail("Send", e);
			}
		}

		public static void Broadcast(Dictionary<int, NetworkConnection> peers, int exceptId, byte[] data, int len)
		{
			if (peers.Count == 0)
			{
				return;
			}
			NetworkWriter val = null;
			foreach (KeyValuePair<int, NetworkConnection> peer in peers)
			{
				if (peer.Key == exceptId)
				{
					continue;
				}
				try
				{
					if (val == null)
					{
						val = BuildWriter(data, len);
					}
					peer.Value.Send(val.ToArraySegment(), 0);
				}
				catch (Exception e)
				{
					Fail("Broadcast", e);
				}
			}
		}

		private static void Fail(string where, Exception e)
		{
			if (errs++ < 5)
			{
				Plugin.Log.LogError((object)("Whiteboard net (" + where + "): " + e));
			}
		}
	}
	internal sealed class ByteWriter
	{
		public byte[] Data = new byte[256];

		public int Length;

		public void Reset()
		{
			Length = 0;
		}

		private void Ensure(int extra)
		{
			int num = Length + extra;
			if (num > Data.Length)
			{
				int num2;
				for (num2 = Data.Length * 2; num2 < num; num2 *= 2)
				{
				}
				Array.Resize(ref Data, num2);
			}
		}

		public void WriteByte(byte v)
		{
			Ensure(1);
			Data[Length++] = v;
		}

		public void WriteShort(short v)
		{
			Ensure(2);
			Data[Length++] = (byte)v;
			Data[Length++] = (byte)(v >> 8);
		}

		public void WriteUShort(ushort v)
		{
			Ensure(2);
			Data[Length++] = (byte)v;
			Data[Length++] = (byte)(v >> 8);
		}

		public void WriteInt(int v)
		{
			Ensure(4);
			Data[Length++] = (byte)v;
			Data[Length++] = (byte)(v >> 8);
			Data[Length++] = (byte)(v >> 16);
			Data[Length++] = (byte)(v >> 24);
		}

		public void WriteFloat(float f)
		{
			WriteInt(BitConverter.SingleToInt32Bits(f));
		}

		public void WriteVarint(uint v)
		{
			while (v >= 128)
			{
				WriteByte((byte)(v | 0x80));
				v >>= 7;
			}
			WriteByte((byte)v);
		}

		public void WriteBytes(byte[] src, int offset, int count)
		{
			Ensure(count);
			Array.Copy(src, offset, Data, Length, count);
			Length += count;
		}
	}
	internal struct ByteReader
	{
		private readonly byte[] d;

		private readonly int len;

		private int pos;

		public bool Overrun;

		public int Remaining => len - pos;

		public ByteReader(byte[] data, int length)
		{
			d = data;
			len = length;
			pos = 0;
			Overrun = false;
		}

		public byte ReadByte()
		{
			if (pos >= len)
			{
				Overrun = true;
				return 0;
			}
			return d[pos++];
		}

		public short ReadShort()
		{
			int num = ReadByte();
			int num2 = ReadByte();
			return (short)(num | (num2 << 8));
		}

		public ushort ReadUShort()
		{
			int num = ReadByte();
			int num2 = ReadByte();
			return (ushort)(num | (num2 << 8));
		}

		public int ReadInt()
		{
			int num = ReadByte();
			int num2 = ReadByte();
			int num3 = ReadByte();
			int num4 = ReadByte();
			return num | (num2 << 8) | (num3 << 16) | (num4 << 24);
		}

		public float ReadFloat()
		{
			return BitConverter.Int32BitsToSingle(ReadInt());
		}

		public uint ReadVarint()
		{
			uint num = 0u;
			int num2 = 0;
			for (int i = 0; i < 5; i++)
			{
				byte b = ReadByte();
				num |= (uint)((b & 0x7F) << num2);
				if ((b & 0x80) == 0)
				{
					return num;
				}
				num2 += 7;
			}
			Overrun = true;
			return num;
		}

		public void ReadBytes(byte[] dest, int count)
		{
			if (count > Remaining)
			{
				Overrun = true;
				return;
			}
			Array.Copy(d, pos, dest, 0, count);
			pos += count;
		}
	}
	internal static class Palette
	{
		public static readonly Color32 Paper = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);

		public static readonly Color32[] Colors = (Color32[])(object)new Color32[12]
		{
			new Color32((byte)15, (byte)15, (byte)15, byte.MaxValue),
			new Color32((byte)128, (byte)128, (byte)128, byte.MaxValue),
			new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue),
			new Color32((byte)220, (byte)30, (byte)30, byte.MaxValue),
			new Color32((byte)240, (byte)140, (byte)20, byte.MaxValue),
			new Color32((byte)245, (byte)220, (byte)40, byte.MaxValue),
			new Color32((byte)30, (byte)160, (byte)60, byte.MaxValue),
			new Color32((byte)30, (byte)190, (byte)210, byte.MaxValue),
			new Color32((byte)30, (byte)90, (byte)220, byte.MaxValue),
			new Color32((byte)140, (byte)50, (byte)190, byte.MaxValue),
			new Color32((byte)240, (byte)120, (byte)180, byte.MaxValue),
			new Color32((byte)120, (byte)72, (byte)36, byte.MaxValue)
		};

		public static readonly string[] Names = new string[12]
		{
			"Black", "Gray", "White", "Red", "Orange", "Yellow", "Green", "Cyan", "Blue", "Purple",
			"Pink", "Brown"
		};

		public static int Count => Colors.Length;
	}
	[BepInPlugin("gogogadgetjustice.bigwalk.bwpaint", "bwpaint", "1.0.0")]
	public class Plugin : BasePlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Multiplayer;

		public override void Load()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BasePlugin)this).Log;
			Multiplayer = ((BasePlugin)this).Config.Bind<bool>("Multiplayer", "Enabled", true, "Share the board with other players who also have this mod. EVERYONE in the lobby should have it: a host without the mod may drop modded clients that send it unknown messages. Turn off to play solo.");
			try
			{
				new Harmony("local.bigwalk.whiteboard").PatchAll();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Whiteboard: Harmony patching failed (cursor may re-lock in draw mode): " + ex));
			}
			((BasePlugin)this).AddComponent<WhiteboardBehaviour>();
			Log.LogInfo((object)("Whiteboard loaded. F10 = place/pick up board, E (aimed at board) = draw mode. Multiplayer: " + Multiplayer.Value));
		}
	}
	internal static class PngWriter
	{
		private static readonly byte[] Signature = new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 };

		private static uint[] crcTable;

		public static byte[] Encode(DrawingSurface s)
		{
			int num = 1280;
			int num2 = 640;
			int num3 = 1 + num * 3;
			byte[] array = new byte[num3 * num2];
			for (int i = 0; i < num2; i++)
			{
				int y = num2 - 1 - i;
				int num4 = i * num3;
				array[num4] = 0;
				s.CopyRowRgb(y, array, num4 + 1);
			}
			byte[] array2 = ZlibCompress(array);
			using MemoryStream memoryStream = new MemoryStream(array2.Length + 128);
			memoryStream.Write(Signature, 0, Signature.Length);
			byte[] array3 = new byte[13];
			WriteBE(array3, 0, (uint)num);
			WriteBE(array3, 4, (uint)num2);
			array3[8] = 8;
			array3[9] = 2;
			array3[10] = 0;
			array3[11] = 0;
			array3[12] = 0;
			WriteChunk(memoryStream, "IHDR", array3);
			WriteChunk(memoryStream, "IDAT", array2);
			WriteChunk(memoryStream, "IEND", new byte[0]);
			return memoryStream.ToArray();
		}

		private static byte[] ZlibCompress(byte[] raw)
		{
			using MemoryStream memoryStream = new MemoryStream(raw.Length / 4 + 64);
			memoryStream.WriteByte(120);
			memoryStream.WriteByte(156);
			using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal, leaveOpen: true))
			{
				deflateStream.Write(raw, 0, raw.Length);
			}
			uint num = Adler32(raw);
			memoryStream.WriteByte((byte)(num >> 24));
			memoryStream.WriteByte((byte)(num >> 16));
			memoryStream.WriteByte((byte)(num >> 8));
			memoryStream.WriteByte((byte)num);
			return memoryStream.ToArray();
		}

		private static uint Adler32(byte[] data)
		{
			uint num = 1u;
			uint num2 = 0u;
			int num3 = 0;
			int num4 = data.Length;
			while (num4 > 0)
			{
				int num5 = ((num4 < 5552) ? num4 : 5552);
				num4 -= num5;
				while (num5-- > 0)
				{
					num += data[num3++];
					num2 += num;
				}
				num %= 65521;
				num2 %= 65521;
			}
			return (num2 << 16) | num;
		}

		private static void WriteChunk(Stream s, string type, byte[] data)
		{
			byte[] array = new byte[4];
			WriteBE(array, 0, (uint)data.Length);
			s.Write(array, 0, 4);
			byte[] array2 = new byte[4];
			for (int i = 0; i < 4; i++)
			{
				array2[i] = (byte)type[i];
			}
			s.Write(array2, 0, 4);
			if (data.Length != 0)
			{
				s.Write(data, 0, data.Length);
			}
			uint crc = Crc32(uint.MaxValue, array2, 4);
			crc = Crc32(crc, data, data.Length) ^ 0xFFFFFFFFu;
			byte[] array3 = new byte[4];
			WriteBE(array3, 0, crc);
			s.Write(array3, 0, 4);
		}

		private static uint Crc32(uint crc, byte[] data, int count)
		{
			if (crcTable == null)
			{
				uint[] array = new uint[256];
				for (uint num = 0u; num < 256; num++)
				{
					uint num2 = num;
					for (int i = 0; i < 8; i++)
					{
						num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1));
					}
					array[num] = num2;
				}
				crcTable = array;
			}
			uint[] array2 = crcTable;
			for (int j = 0; j < count; j++)
			{
				crc = array2[(crc ^ data[j]) & 0xFF] ^ (crc >> 8);
			}
			return crc;
		}

		private static void WriteBE(byte[] b, int o, uint v)
		{
			b[o] = (byte)(v >> 24);
			b[o + 1] = (byte)(v >> 16);
			b[o + 2] = (byte)(v >> 8);
			b[o + 3] = (byte)v;
		}
	}
	internal static class ShapeDrawing
	{
		public static void Line(DrawingSurface s, int x0, int y0, int x1, int y1, int r, Color32 c)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			float num = x1 - x0;
			float num2 = y1 - y0;
			float num3 = Mathf.Sqrt(num * num + num2 * num2);
			int num4 = Math.Max(1, (int)(num3 / Math.Max(1f, (float)r * 0.5f)));
			for (int i = 0; i <= num4; i++)
			{
				float num5 = (float)i / (float)num4;
				s.Stamp(x0 + Mathf.RoundToInt(num * num5), y0 + Mathf.RoundToInt(num2 * num5), r, c);
			}
		}

		public static void Rect(DrawingSurface s, int x0, int y0, int x1, int y1, int r, Color32 c, bool filled)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Min(x0, x1);
			int num2 = Math.Max(x0, x1);
			int num3 = Math.Min(y0, y1);
			int num4 = Math.Max(y0, y1);
			if (filled)
			{
				for (int i = num3; i <= num4; i++)
				{
					s.FillSpan(i, num, num2, c);
				}
			}
			else
			{
				Line(s, num, num3, num2, num3, r, c);
				Line(s, num2, num3, num2, num4, r, c);
				Line(s, num2, num4, num, num4, r, c);
				Line(s, num, num4, num, num3, r, c);
			}
		}

		public static void Ellipse(DrawingSurface s, int x0, int y0, int x1, int y1, int r, Color32 c, bool filled)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Min(x0, x1);
			int num2 = Math.Max(x0, x1);
			int num3 = Math.Min(y0, y1);
			int num4 = Math.Max(y0, y1);
			float num5 = (float)(num + num2) * 0.5f;
			float num6 = (float)(num3 + num4) * 0.5f;
			float num7 = (float)(num2 - num) * 0.5f;
			float num8 = (float)(num4 - num3) * 0.5f;
			if (num7 < 0.5f || num8 < 0.5f)
			{
				Line(s, num, num3, num2, num4, r, c);
			}
			else if (filled)
			{
				for (int i = num3; i <= num4; i++)
				{
					float num9 = ((float)i - num6) / num8;
					float num10 = 1f - num9 * num9;
					if (!(num10 < 0f))
					{
						float num11 = num7 * Mathf.Sqrt(num10);
						s.FillSpan(i, Mathf.RoundToInt(num5 - num11), Mathf.RoundToInt(num5 + num11), c);
					}
				}
			}
			else
			{
				float num12 = (float)Math.PI * 2f * Mathf.Sqrt((num7 * num7 + num8 * num8) * 0.5f);
				int num13 = Math.Max(16, (int)(num12 / Math.Max(1f, (float)r * 0.5f)));
				float num14 = (float)Math.PI * 2f / (float)num13;
				for (int j = 0; j < num13; j++)
				{
					float num15 = (float)j * num14;
					s.Stamp(Mathf.RoundToInt(num5 + num7 * Mathf.Cos(num15)), Mathf.RoundToInt(num6 + num8 * Mathf.Sin(num15)), r, c);
				}
			}
		}
	}
	internal static class SnapshotCodec
	{
		public static void Encode(DrawingSurface s, ByteWriter w)
		{
			//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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 640; i++)
			{
				int k;
				for (int j = 0; j < 1280; j += k)
				{
					Color32 pixel = s.GetPixel(j, i);
					for (k = 1; j + k < 1280 && Same(s.GetPixel(j + k, i), pixel); k++)
					{
					}
					w.WriteVarint((uint)k);
					w.WriteByte(pixel.r);
					w.WriteByte(pixel.g);
					w.WriteByte(pixel.b);
				}
			}
		}

		public static void Decode(DrawingSurface s, byte[] data, int len)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			s.BeginRawLoad();
			ByteReader byteReader = new ByteReader(data, len);
			for (int i = 0; i < 640; i++)
			{
				int num;
				for (int j = 0; j < 1280; j += num)
				{
					num = (int)byteReader.ReadVarint();
					byte b = byteReader.ReadByte();
					byte b2 = byteReader.ReadByte();
					byte b3 = byteReader.ReadByte();
					if (byteReader.Overrun || num <= 0)
					{
						return;
					}
					int num2 = Math.Min(1280, j + num);
					s.RawFillSpan(i, j, num2 - 1, new Color32(b, b2, b3, byte.MaxValue));
				}
			}
		}

		private static bool Same(Color32 a, Color32 b)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			return a.r == b.r && a.g == b.g && a.b == b.b;
		}
	}
	internal sealed class StrokeSender
	{
		private const byte FlagBegin = 1;

		private const byte FlagEnd = 2;

		private const byte FlagFresh = 4;

		private const float FlushInterval = 0.05f;

		private readonly WhiteboardNet net;

		private readonly short[] xs = new short[64];

		private readonly short[] ys = new short[64];

		private int count;

		private int radius;

		private Color32 color;

		private bool active;

		private bool begin;

		private bool fresh;

		private float nextFlush;

		public StrokeSender(WhiteboardNet net)
		{
			this.net = net;
		}

		public void Begin(int r, Color32 c, float now)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			radius = r;
			color = c;
			count = 0;
			active = true;
			begin = true;
			fresh = true;
			nextFlush = now + 0.05f;
		}

		public void Add(int x, int y, float now)
		{
			if (active)
			{
				if (count == xs.Length)
				{
					Flush(now, end: false);
				}
				xs[count] = (short)x;
				ys[count] = (short)y;
				count++;
				if (now >= nextFlush)
				{
					Flush(now, end: false);
				}
			}
		}

		public void Lift(float now)
		{
			if (active)
			{
				Flush(now, end: false);
				fresh = true;
			}
		}

		public void End(float now)
		{
			if (active)
			{
				Flush(now, end: true);
				active = false;
			}
		}

		private void Flush(float now, bool end)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (count != 0 || (end && !begin))
			{
				byte b = 0;
				if (begin)
				{
					b |= 1;
				}
				if (end)
				{
					b |= 2;
				}
				if (fresh && count > 0)
				{
					b |= 4;
				}
				net.SendPoints(b, radius, color, xs, ys, count);
				count = 0;
				begin = false;
				fresh = false;
				nextFlush = now + 0.05f;
			}
		}
	}
	internal enum Tool
	{
		Pencil,
		Eraser,
		Line,
		Rect,
		Ellipse,
		Fill,
		Picker
	}
	internal static class ToolInfo
	{
		public static readonly string[] Names = new string[7] { "Pencil", "Eraser", "Line", "Rect", "Ellipse", "Fill", "Picker" };

		public static int Count => Names.Length;

		public static bool IsShape(Tool t)
		{
			return t == Tool.Line || t == Tool.Rect || t == Tool.Ellipse;
		}
	}
	public class WhiteboardBehaviour : MonoBehaviour
	{
		private const float UseRange = 3.5f;

		private const float DrawRange = 12f;

		private const int MinBrush = 1;

		private const int MaxBrush = 40;

		private const float FlashSeconds = 0.6f;

		private const float ResizeStep = 0.05f;

		private const float ResizeRepeat = 0.06f;

		private const float PlaceSendInterval = 0.1f;

		private static readonly KeyCode[] ToolKeys;

		private readonly BoardSession session = new BoardSession();

		private readonly WhiteboardNet net;

		private readonly StrokeSender sender;

		private readonly InfoPad pad = new InfoPad();

		private Camera cam;

		private bool drawMode;

		private bool aimed;

		private bool strokeOpen;

		private bool hasPrev;

		private bool shapeStarted;

		private bool cancelled;

		private int prevX;

		private int prevY;

		private int startX;

		private int startY;

		private int lastEx;

		private int lastEy;

		private Tool tool = Tool.Pencil;

		private Tool prevTool = Tool.Pencil;

		private int paletteIdx;

		private Color32 currentColor = Palette.Colors[0];

		private bool shapeFilled;

		private int brush = 4;

		private float nextResize;

		private bool placeDirty;

		private float nextPlaceSend;

		private int errCount;

		public WhiteboardBehaviour(IntPtr ptr)
			: base(ptr)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			net = new WhiteboardNet(session);
			sender = new StrokeSender(net);
		}

		private void Update()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				net.Tick(realtimeSinceStartup);
				if (Input.GetKeyDown((KeyCode)291))
				{
					TogglePlaced();
				}
				WhiteboardBoard board = session.Board;
				if (board == null || !board.Active)
				{
					if (drawMode)
					{
						ExitDrawMode();
					}
					aimed = false;
					return;
				}
				Camera camera = GetCamera();
				if ((Object)(object)camera == (Object)null)
				{
					return;
				}
				float time = Time.time;
				aimed = !drawMode && board.IsAimedAt(camera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f)), 3.5f);
				if (Input.GetKeyDown((KeyCode)101))
				{
					if (drawMode)
					{
						ExitDrawMode();
					}
					else if (aimed && net.BeginDrawing(realtimeSinceStartup))
					{
						EnterDrawMode();
					}
				}
				if (Input.GetKeyDown((KeyCode)292))
				{
					SavePng(time, board);
				}
				if (drawMode)
				{
					if (net.Lock == LockState.Other)
					{
						ExitDrawMode();
					}
					else
					{
						HandleKeys(time, realtimeSinceStartup, board);
						HandleMouse(camera, time, realtimeSinceStartup, board);
						pad.SetState(tool, paletteIdx, (tool == Tool.Eraser) ? Palette.Paper : currentColor, brush, shapeFilled, board.Scale, session.Surface.UndoCount);
						FlushPlace(realtimeSinceStartup, board);
					}
				}
				session.Surface.Flush();
				board.Tick(time);
			}
			catch (Exception ex)
			{
				if (errCount++ < 5)
				{
					Plugin.Log.LogError((object)("Whiteboard Update: " + ex));
				}
			}
		}

		private void OnGUI()
		{
			try
			{
				WhiteboardBoard board = session.Board;
				if (board != null && board.Active)
				{
					PadMode mode = (drawMode ? PadMode.Full : (aimed ? PadMode.Hint : PadMode.None));
					pad.Draw(mode, Time.time, (net.Lock == LockState.Other) ? "Someone else is drawing" : "Press E to draw on the board");
				}
			}
			catch (Exception ex)
			{
				if (errCount++ < 5)
				{
					Plugin.Log.LogError((object)("Whiteboard OnGUI: " + ex));
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				ExitDrawMode();
			}
			catch
			{
			}
		}

		private Camera GetCamera()
		{
			if ((Object)(object)cam == (Object)null)
			{
				cam = Camera.main;
			}
			return cam;
		}

		private void TogglePlaced()
		{
			if (net.Lock == LockState.Other)
			{
				return;
			}
			WhiteboardBoard board = session.Board;
			if (board != null && board.Active)
			{
				if (drawMode)
				{
					ExitDrawMode();
				}
				board.SetActive(on: false);
				SendPlaceState();
				return;
			}
			Camera camera = GetCamera();
			if ((Object)(object)camera == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Whiteboard: no Camera.main");
				return;
			}
			session.Ensure();
			board = session.Board;
			board.PlaceInFront(camera);
			board.SetActive(on: true);
			SendPlaceState();
		}

		private void SendPlaceState()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			WhiteboardBoard board = session.Board;
			if (board != null && board.Built)
			{
				board.GetPlacement(out var pos, out var yaw);
				net.SendPlace(board.Active, pos, yaw, board.Scale);
				placeDirty = false;
			}
		}

		private void FlushPlace(float rt, WhiteboardBoard board)
		{
			if (placeDirty && !(rt < nextPlaceSend))
			{
				nextPlaceSend = rt + 0.1f;
				SendPlaceState();
			}
		}

		private void EnterDrawMode()
		{
			if (!drawMode)
			{
				drawMode = true;
				hasPrev = false;
				CursorBlock.Active = true;
				RewiredLock.Set(lockInput: true);
				CursorBlock.SetCursor(free: true);
			}
		}

		private void ExitDrawMode()
		{
			if (strokeOpen && session.Surface != null)
			{
				FinishStroke(session.Surface, Time.realtimeSinceStartup);
			}
			hasPrev = false;
			shapeStarted = false;
			if (drawMode)
			{
				drawMode = false;
				if (placeDirty)
				{
					SendPlaceState();
				}
				net.EndDrawing();
				RewiredLock.Set(lockInput: false);
				CursorBlock.Active = false;
				CursorBlock.SetCursor(free: false);
			}
		}

		private void HandleKeys(float now, float rt, WhiteboardBoard board)
		{
			DrawingSurface surface = session.Surface;
			if (!strokeOpen)
			{
				for (int i = 0; i < ToolKeys.Length; i++)
				{
					if (Input.GetKeyDown(ToolKeys[i]))
					{
						SelectTool((Tool)i);
					}
				}
				if (Input.GetKeyDown((KeyCode)102))
				{
					shapeFilled = !shapeFilled;
				}
				if (Input.GetKeyDown((KeyCode)117) && surface.Undo())
				{
					net.SendUndo();
					pad.Say("Undo", now);
				}
				if (Input.GetKeyDown((KeyCode)127))
				{
					surface.Wipe();
					net.SendWipe();
					pad.Say("Board wiped", now);
				}
				if (Input.GetKeyDown((KeyCode)99))
				{
					CycleColor(-1);
				}
				if (Input.GetKeyDown((KeyCode)118))
				{
					CycleColor(1);
				}
				if (Input.GetKeyDown((KeyCode)91))
				{
					brush = Math.Max(1, brush - 1);
				}
				if (Input.GetKeyDown((KeyCode)93))
				{
					brush = Math.Min(40, brush + 1);
				}
			}
			int num = 0;
			if (Input.GetKey((KeyCode)61) || Input.GetKey((KeyCode)270))
			{
				num = 1;
			}
			else if (Input.GetKey((KeyCode)45) || Input.GetKey((KeyCode)269))
			{
				num = -1;
			}
			if (num != 0 && now >= nextResize)
			{
				nextResize = now + 0.06f;
				board.SetScale(board.Scale + (float)num * 0.05f);
				placeDirty = true;
			}
		}

		private void SelectTool(Tool t)
		{
			if (t == Tool.Picker && tool != Tool.Picker)
			{
				prevTool = tool;
			}
			tool = t;
		}

		private void CycleColor(int delta)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (paletteIdx < 0)
			{
				paletteIdx = ((delta <= 0) ? (Palette.Count - 1) : 0);
			}
			else
			{
				paletteIdx = (paletteIdx + delta + Palette.Count) % Palette.Count;
			}
			currentColor = Palette.Colors[paletteIdx];
		}

		private int EffRadius()
		{
			return (tool == Tool.Eraser) ? Math.Min(80, brush * 3) : brush;
		}

		private Color32 EffColor()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return (tool == Tool.Eraser) ? Palette.Paper : currentColor;
		}

		private void HandleMouse(Camera camera, float now, float rt, WhiteboardBoard board)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			DrawingSurface surface = session.Surface;
			if (!Input.GetMouseButton(0))
			{
				if (strokeOpen)
				{
					FinishStroke(surface, rt);
				}
				hasPrev = false;
				shapeStarted = false;
				cancelled = false;
				return;
			}
			bool mouseButtonDown = Input.GetMouseButtonDown(0);
			if (!strokeOpen)
			{
				surface.BeginStroke();
				strokeOpen = true;
				if (tool == Tool.Pencil || tool == Tool.Eraser)
				{
					sender.Begin(EffRadius(), EffColor(), rt);
				}
			}
			if (cancelled)
			{
				return;
			}
			if (shapeStarted && Input.GetMouseButtonDown(1))
			{
				surface.RevertStroke();
				shapeStarted = false;
				cancelled = true;
				return;
			}
			Ray ray = camera.ScreenPointToRay(Input.mousePosition);
			float u;
			float v;
			bool flag = board.TryRaycast(ray, 12f, out u, out v);
			int num = 0;
			int num2 = 0;
			if (flag)
			{
				num = (int)(u * 1279f + 0.5f);
				num2 = (int)(v * 639f + 0.5f);
			}
			switch (tool)
			{
			case Tool.Pencil:
			case Tool.Eraser:
				DrawFreehand(surface, flag, num, num2, rt);
				break;
			case Tool.Line:
			case Tool.Rect:
			case Tool.Ellipse:
				DrawShape(surface, flag, num, num2);
				break;
			case Tool.Fill:
				if (mouseButtonDown && flag)
				{
					FloodFill.Fill(surface, num, num2, currentColor);
					net.SendFill(num, num2, currentColor);
				}
				break;
			case Tool.Picker:
				if (mouseButtonDown && flag)
				{
					Pick(surface, num, num2, now);
				}
				break;
			}
		}

		private void FinishStroke(DrawingSurface surface, float rt)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (ToolInfo.IsShape(tool) && shapeStarted && lastEx >= 0)
			{
				net.SendShape((byte)tool, brush, currentColor, shapeFilled, startX, startY, lastEx, lastEy);
			}
			else if (tool == Tool.Pencil || tool == Tool.Eraser)
			{
				sender.End(rt);
			}
			surface.EndStroke();
			strokeOpen = false;
		}

		private void DrawFreehand(DrawingSurface surface, bool hit, int px, int py, float rt)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!hit)
			{
				if (hasPrev)
				{
					sender.Lift(rt);
				}
				hasPrev = false;
				return;
			}
			int r = EffRadius();
			Color32 c = EffColor();
			if (hasPrev)
			{
				ShapeDrawing.Line(surface, prevX, prevY, px, py, r, c);
			}
			else
			{
				surface.Stamp(px, py, r, c);
			}
			sender.Add(px, py, rt);
			prevX = px;
			prevY = py;
			hasPrev = true;
		}

		private void DrawShape(DrawingSurface surface, bool hit, int px, int py)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			if (!hit)
			{
				return;
			}
			if (!shapeStarted)
			{
				shapeStarted = true;
				startX = px;
				startY = py;
				lastEx = -1;
				lastEy = -1;
			}
			if (px != lastEx || py != lastEy)
			{
				lastEx = px;
				lastEy = py;
				surface.RevertStroke();
				switch (tool)
				{
				case Tool.Line:
					ShapeDrawing.Line(surface, startX, startY, px, py, brush, currentColor);
					break;
				case Tool.Rect:
					ShapeDrawing.Rect(surface, startX, startY, px, py, brush, currentColor, shapeFilled);
					break;
				case Tool.Ellipse:
					ShapeDrawing.Ellipse(surface, startX, startY, px, py, brush, currentColor, shapeFilled);
					break;
				}
			}
		}

		private void Pick(DrawingSurface surface, int px, int py, float now)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			currentColor = surface.GetPixel(px, py);
			paletteIdx = -1;
			tool = prevTool;
			pad.Say($"Picked #{currentColor.r:X2}{currentColor.g:X2}{currentColor.b:X2}", now);
		}

		private void SavePng(float now, WhiteboardBoard board)
		{
			try
			{
				string text = session.Surface.SavePng();
				board.Flash(now, 0.6f);
				pad.Say("Saved: " + text, now);
				Plugin.Log.LogInfo((object)("Whiteboard saved: " + text));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Whiteboard save failed: " + ex));
			}
		}

		static WhiteboardBehaviour()
		{
			KeyCode[] array = new KeyCode[7];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			ToolKeys = (KeyCode[])(object)array;
		}
	}
	internal sealed class WhiteboardBoard
	{
		private const float FaceW = 2.4f;

		private const float FaceH = 1.2f;

		private const float FrameW = 2.5f;

		private const float FrameH = 1.3f;

		private const float FrameD = 0.04f;

		private const float BottomOffset = 0.55f;

		public const float MinScale = 0.5f;

		public const float MaxScale = 4f;

		private static readonly Color32 FrameColor = new Color32((byte)70, (byte)70, (byte)70, byte.MaxValue);

		private static readonly Color32 FlashColor = new Color32((byte)40, (byte)200, (byte)60, byte.MaxValue);

		private readonly DrawingSurface surface;

		private GameObject root;

		private Transform frameT;

		private Transform faceT;

		private Collider col;

		private Material frameMat;

		private float bottomY;

		private bool flashing;

		private float flashUntil;

		public float Scale { get; private set; } = 1f;

		public bool Built => (Object)(object)root != (Object)null;

		public bool Active => (Object)(object)root != (Object)null && root.activeSelf;

		public WhiteboardBoard(DrawingSurface surface)
		{
			this.surface = surface;
		}

		public void SetActive(bool on)
		{
			if ((Object)(object)root != (Object)null)
			{
				root.SetActive(on);
			}
		}

		public void EnsureBuilt()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)root != (Object)null))
			{
				root = new GameObject("BW_Whiteboard");
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)val).name = "BW_Frame";
				frameT = val.transform;
				frameT.SetParent(root.transform, false);
				col = val.GetComponent<Collider>();
				frameMat = MaterialUtil.Make(FrameColor, null);
				if ((Object)(object)frameMat != (Object)null)
				{
					val.GetComponent<Renderer>().material = frameMat;
				}
				GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)5);
				((Object)val2).name = "BW_Face";
				faceT = val2.transform;
				faceT.SetParent(root.transform, false);
				faceT.localPosition = new Vector3(0f, 0f, -0.022f);
				Collider component = val2.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				Material val3 = MaterialUtil.Make(Palette.Paper, (Texture)(object)surface.Tex);
				if ((Object)(object)val3 != (Object)null)
				{
					val2.GetComponent<Renderer>().material = val3;
				}
				ApplyScale();
				root.SetActive(false);
			}
		}

		public void PlaceInFront(Camera cam)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: 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_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			Vector3 forward = ((Component)cam).transform.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.001f)
			{
				forward = Vector3.forward;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 val = ((Component)cam).transform.position + forward * 2.5f;
			float num = val.y - 1.4f;
			Il2CppStructArray<RaycastHit> val2 = Physics.RaycastAll(val, Vector3.down, 6f, -1, (QueryTriggerInteraction)1);
			float num2 = float.MaxValue;
			for (int i = 0; i < ((Il2CppArrayBase<RaycastHit>)(object)val2).Length; i++)
			{
				RaycastHit val3 = ((Il2CppArrayBase<RaycastHit>)(object)val2)[i];
				if (!((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null) && !((Object)((RaycastHit)(ref val3)).collider).name.Contains("Plane") && ((RaycastHit)(ref val3)).distance < num2)
				{
					num2 = ((RaycastHit)(ref val3)).distance;
					num = ((RaycastHit)(ref val3)).point.y;
				}
			}
			bottomY = num + 0.55f;
			root.transform.SetPositionAndRotation(new Vector3(val.x, bottomY + 1.3f * Scale * 0.5f, val.z), Quaternion.LookRotation(forward));
		}

		public void GetPlacement(out Vector3 pos, out float yaw)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			pos = root.transform.position;
			yaw = root.transform.eulerAngles.y;
		}

		public void SetPlacement(Vector3 pos, float yaw, float scale)
		{
			//IL_0019: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			Scale = Mathf.Clamp(scale, 0.5f, 4f);
			bottomY = pos.y - 1.3f * Scale * 0.5f;
			root.transform.SetPositionAndRotation(pos, Quaternion.Euler(0f, yaw, 0f));
			ApplyScale();
		}

		public void SetScale(float s)
		{
			s = Mathf.Clamp(s, 0.5f, 4f);
			if (!Mathf.Approximately(s, Scale))
			{
				Scale = s;
				ApplyScale();
			}
		}

		private void ApplyScale()
		{
			//IL_0039: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)root == (Object)null))
			{
				frameT.localScale = new Vector3(2.5f * Scale, 1.3f * Scale, 0.04f);
				faceT.localScale = new Vector3(2.4f * Scale, 1.2f * Scale, 1f);
				Vector3 position = root.transform.position;
				root.transform.position = new Vector3(position.x, bottomY + 1.3f * Scale * 0.5f, position.z);
			}
		}

		public bool IsAimedAt(Ray ray, float range)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (Vector3.Dot(((Ray)(ref ray)).direction, root.transform.forward) <= 0f)
			{
				return false;
			}
			RaycastHit val = default(RaycastHit);
			return col.Raycast(ray, ref val, range);
		}

		public bool TryRaycast(Ray ray, float range, out float u, out float v)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			u = 0f;
			v = 0f;
			if (Vector3.Dot(((Ray)(ref ray)).direction, root.transform.forward) <= 0f)
			{
				return false;
			}
			RaycastHit val = default(RaycastHit);
			if (!col.Raycast(ray, ref val, range))
			{
				return false;
			}
			Vector3 val2 = faceT.InverseTransformPoint(((RaycastHit)(ref val)).point);
			if (val2.x < -0.5f || val2.x > 0.5f || val2.y < -0.5f || val2.y > 0.5f)
			{
				return false;
			}
			u = val2.x + 0.5f;
			v = val2.y + 0.5f;
			return true;
		}

		public void Flash(float now, float seconds)
		{
			flashUntil = now + seconds;
		}

		public void Tick(float now)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)frameMat == (Object)null))
			{
				bool flag = now < flashUntil;
				if (flag != flashing)
				{
					flashing = flag;
					MaterialUtil.SetColor(frameMat, flag ? FlashColor : FrameColor);
				}
			}
		}
	}
	internal enum LockState : byte
	{
		Free,
		Other,
		You
	}
	internal sealed class WhiteboardNet
	{
		private const byte OpHello = 1;

		private const byte OpLockReq = 2;

		private const byte OpLockRel = 3;

		private const byte OpLockState = 129;

		private const byte OpSnap = 144;

		private const byte OpPoints = 16;

		private const byte OpShape = 17;

		private const byte OpFill = 18;

		private const byte OpUndo = 19;

		private const byte OpWipe = 20;

		private const byte OpPlace = 21;

		private const int HostId = 0;

		private const float LockSeconds = 6f;

		private const float HeartbeatSeconds = 1.5f;

		private const float HelloDelay = 1f;

		private const float HelloRetry = 2.5f;

		private const float HousekeepSeconds = 1f;

		private const int SnapChunk = 3000;

		private readonly BoardSession session;

		private readonly ByteWriter w = new ByteWriter();

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

		private readonly List<int> scratch = new List<int>();

		private readonly short[] pxs = new short[256];

		private readonly short[] pys = new short[256];

		private int holder = -1;

		private float holderUntil;

		private bool wasHost;

		private bool joined;

		private bool wasClient;

		private bool wantHold;

		private float nextHello;

		private float nextBeat;

		private int snapId = -1;

		private int snapGot;

		private byte[][] snapParts;

		private byte nextSnapId;

		private float nextHousekeep;

		private float nextHandlerCheck;

		private int errs;

		public LockState Lock { get; private set; } = LockState.Free;

		public bool Enabled => Plugin.Multiplayer.Value && MirrorTransport.Networked;

		private bool Ready => Enabled && (MirrorTransport.IsHost || joined);

		public WhiteboardNet(BoardSession session)
		{
			this.session = session;
			MirrorTransport.ServerReceived = OnServerMessage;
			MirrorTransport.ClientReceived = OnClientMessage;
		}

		public void Tick(float now)
		{
			if (!Plugin.Multiplayer.Value)
			{
				return;
			}
			if (now >= nextHandlerCheck)
			{
				nextHandlerCheck = now + 1f;
				MirrorTransport.EnsureHandlers();
			}
			if (MirrorTransport.IsHost)
			{
				wasHost = true;
				HostTick(now);
				return;
			}
			if (wasHost)
			{
				wasHost = false;
				ResetHost();
			}
			ClientTick(now);
		}

		private void HostTick(float now)
		{
			if (holder == 0 && wantHold)
			{
				holderUntil = now + 6f;
			}
			if (holder != -1 && now > holderUntil)
			{
				ReleaseHolder();
			}
			if (now < nextHousekeep)
			{
				return;
			}
			nextHousekeep = now + 1f;
			scratch.Clear();
			foreach (KeyValuePair<int, NetworkConnection> peer in peers)
			{
				if (!MirrorTransport.ConnectionAlive(peer.Key))
				{
					scratch.Add(peer.Key);
				}
			}
			for (int i = 0; i < scratch.Count; i++)
			{
				peers.Remove(scratch[i]);
				if (holder == scratch[i])
				{
					ReleaseHolder();
				}
			}
		}

		private void ResetHost()
		{
			peers.Clear();
			holder = -1;
			Lock = LockState.Free;
		}

		private void ClientTick(float now)
		{
			if (!MirrorTransport.ClientConnected)
			{
				if (wasClient)
				{
					wasClient = false;
					joined = false;
					Lock = LockState.Free;
					session.EndRemoteStroke();
				}
				return;
			}
			if (!wasClient)
			{
				wasClient = true;
				joined = false;
				nextHello = now + 1f;
			}
			if (!joined && now >= nextHello)
			{
				nextHello = now + 2.5f;
				w.Reset();
				w.WriteByte(1);
				MirrorTransport.SendToServer(w.Data, w.Length);
			}
			if (joined && wantHold && now >= nextBeat)
			{
				nextBeat = now + 1.5f;
				SendSimple(2);
			}
		}

		public bool BeginDrawing(float now)
		{
			if (!Enabled)
			{
				return true;
			}
			if (MirrorTransport.IsHost)
			{
				if (!GrantOrRefresh(0, now))
				{
					return false;
				}
				wantHold = true;
				return true;
			}
			if (!joined)
			{
				return true;
			}
			if (Lock == LockState.Other)
			{
				return false;
			}
			wantHold = true;
			nextBeat = now + 1.5f;
			Lock = LockState.You;
			SendSimple(2);
			return true;
		}

		public void EndDrawing()
		{
			if (!wantHold)
			{
				return;
			}
			wantHold = false;
			if (!Enabled)
			{
				return;
			}
			if (MirrorTransport.IsHost)
			{
				if (holder == 0)
				{
					ReleaseHolder();
				}
			}
			else if (joined)
			{
				SendSimple(3);
				if (Lock == LockState.You)
				{
					Lock = LockState.Free;
				}
			}
		}

		private bool GrantOrRefresh(int id, float now)
		{
			if (holder != -1 && holder != id && now <= holderUntil)
			{
				return false;
			}
			bool flag = holder != id;
			holder = id;
			holderUntil = now + 6f;
			if (flag)
			{
				BroadcastLockStates();
			}
			return true;
		}

		private void ReleaseHolder()
		{
			holder = -1;
			session.EndRemoteStroke();
			BroadcastLockStates();
		}

		private LockState StateFor(int id)
		{
			if (holder == -1)
			{
				return LockState.Free;
			}
			return (holder != id) ? LockState.Other : LockState.You;
		}

		private void BroadcastLockStates()
		{
			Lock = StateFor(0);
			foreach (KeyValuePair<int, NetworkConnection> peer in peers)
			{
				SendLockState(peer.Value, StateFor(peer.Key));
			}
		}

		private void SendLockState(NetworkConnection c, LockState s)
		{
			w.Reset();
			w.WriteByte(129);
			w.WriteByte((byte)s);
			MirrorTransport.Send(c, w.Data, w.Length);
		}

		private void SendSimple(byte op)
		{
			w.Reset();
			w.WriteByte(op);
			MirrorTransport.SendToServer(w.Data, w.Length);
		}

		private void OnServerMessage(NetworkConnection conn, byte[] d, int n)
		{
			if (!Plugin.Multiplayer.Value || n < 1)
			{
				return;
			}
			try
			{
				int connectionId = conn.connectionId;
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				byte b = d[0];
				switch (b)
				{
				case 1:
					peers[connectionId] = conn;
					SendState(conn, connectionId);
					Plugin.Log.LogInfo((object)("Whiteboard: peer " + connectionId + " joined"));
					return;
				case 2:
					peers[connectionId] = conn;
					if (!GrantOrRefresh(connectionId, realtimeSinceStartup))
					{
						SendLockState(conn, LockState.Other);
					}
					return;
				case 3:
					if (holder == connectionId)
					{
						ReleaseHolder();
					}
					return;
				}
				if (!peers.ContainsKey(connectionId))
				{
					return;
				}
				bool num;
				if (b != 21)
				{
					num = holder == connectionId;
				}
				else
				{
					if (holder == -1)
					{
						goto IL_0121;
					}
					num = holder == connectionId;
				}
				if (!num)
				{
					return;
				}
				goto IL_0121;
				IL_0121:
				ApplyOp(d, n);
				MirrorTransport.Broadcast(peers, connectionId, d, n);
				if (holder == connectionId)
				{
					holderUntil = realtimeSinceStartup + 6f;
				}
			}
			catch (Exception e)
			{
				Fail("server msg", e);
			}
		}

		private void SendState(NetworkConnection conn, int id)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			SendLockState(conn, StateFor(id));
			WhiteboardBoard board = session.Board;
			if (board != null && board.Built)
			{
				board.GetPlacement(out var pos, out var yaw);
				BuildPlace(board.Active, pos, yaw, board.Scale);
				MirrorTransport.Send(conn, w.Data, w.Length);
			}
			if (session.Surface != null && session.Surface.IsAlive)
			{
				SendSnapshot(conn);
			}
		}

		private void SendSnapshot(NetworkConnection conn)
		{
			ByteWriter byteWriter = new ByteWriter();
			SnapshotCodec.Encode(session.Surface, byteWriter);
			int length = byteWriter.Length;
			int num = (length + 3000 - 1) / 3000;
			if (num == 0)
			{
				num = 1;
			}
			byte v = nextSnapId++;
			for (int i = 0; i < num; i++)
			{
				int num2 = i * 3000;
				int count = Math.Min(3000, length - num2);
				w.Reset();
				w.WriteByte(144);
				w.WriteByte(v);
				w.WriteUShort((ushort)i);
				w.WriteUShort((ushort)num);
				w.WriteBytes(byteWriter.Data, num2, count);
				MirrorTransport.Send(conn, w.Data, w.Length);
			}
		}

		private void OnClientMessage(byte[] d, int n)
		{
			if (!Plugin.Multiplayer.Value || n < 1)
			{
				return;
			}
			try
			{
				switch (d[0])
				{
				case 129:
				{
					if (n < 2)
					{
						break;
					}
					joined = true;
					LockState lockState = (LockState)d[1];
					if (Lock != lockState)
					{
						Lock = lockState;
						if (lockState != LockState.You)
						{
							session.EndRemoteStroke();
						}
					}
					break;
				}
				case 144:
					ReceiveSnap(d, n);
					break;
				default:
					ApplyOp(d, n);
					break;
				}
			}
			catch (Exception e)
			{
				Fail("client msg", e);
			}
		}

		private void ReceiveSnap(byte[] d, int n)
		{
			ByteReader byteReader = new ByteReader(d, n);
			byteReader.ReadByte();
			int num = byteReader.ReadByte();
			int num2 = byteReader.ReadUShort();
			int num3 = byteReader.ReadUShort();
			if (byteReader.Overrun || num3 <= 0 || num2 >= num3)
			{
				return;
			}
			if (num != snapId || snapParts == null || snapParts.Length != num3)
			{
				snapId = num;
				snapParts = new byte[num3][];
				snapGot = 0;
			}
			if (snapParts[num2] == null)
			{
				int remaining = byteReader.Remaining;
				byte[] array = new byte[remaining];
				byteReader.ReadBytes(array, remaining);
				snapParts[num2] = array;
				snapGot++;
			}
			if (snapGot >= num3)
			{
				int num4 = 0;
				for (int i = 0; i < num3; i++)
				{
					num4 += snapParts[i].Length;
				}
				byte[] array2 = new byte[num4];
				int num5 = 0;
				for (int j = 0; j < num3; j++)
				{
					Array.Copy(snapParts[j], 0, array2, num5, snapParts[j].Length);
					num5 += snapParts[j].Length;
				}
				snapParts = null;
				snapId = -1;
				session.ApplySnapshot(array2, num4);
				Plugin.Log.LogInfo((object)("Whiteboard: board snapshot received (" + num4 + " bytes)"));
			}
		}

		private void ApplyOp(byte[] d, int n)
		{
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			ByteReader byteReader = new ByteReader(d, n);
			switch (byteReader.ReadByte())
			{
			case 16:
			{
				byte flags = byteReader.ReadByte();
				int radius2 = byteReader.ReadByte();
				Color32 c3 = default(Color32);
				((Color32)(ref c3))..ctor(byteReader.ReadByte(), byteReader.ReadByte(), byteReader.ReadByte(), byte.MaxValue);
				int num = byteReader.ReadByte();
				for (int i = 0; i < num; i++)
				{
					pxs[i] = byteReader.ReadShort();
					pys[i] = byteReader.ReadShort();
				}
				if (!byteReader.Overrun)
				{
					session.ApplyPoints(flags, radius2, c3, pxs, pys, num);
				}
				break;
			}
			case 17:
			{
				byte tool = byteReader.ReadByte();
				int radius = byteReader.ReadByte();
				Color32 c = default(Color32);
				((Color32)(ref c))..ctor(byteReader.ReadByte(), byteReader.ReadByte(), byteReader.ReadByte(), byte.MaxValue);
				bool filled = byteReader.ReadByte() != 0;
				int x = byteReader.ReadShort();
				int y = byteReader.ReadShort();
				int x2 = byteReader.ReadShort();
				int y2 = byteReader.ReadShort();
				if (!byteReader.Overrun)
				{
					session.ApplyShape(tool, radius, c, filled, x, y, x2, y2);
				}
				break;
			}
			case 18:
			{
				int x3 = byteReader.ReadShort();
				int y3 = byteReader.ReadShort();
				Color32 c2 = default(Color32);
				((Color32)(ref c2))..ctor(byteReader.ReadByte(), byteReader.ReadByte(), byteReader.ReadByte(), byte.MaxValue);
				if (!byteReader.Overrun)
				{
					session.ApplyFill(x3, y3, c2);
				}
				break;
			}
			case 19:
				session.ApplyUndo();
				break;
			case 20:
				session.ApplyWipe();
				break;
			case 21:
			{
				bool active = byteReader.ReadByte() != 0;
				Vector3 pos = default(Vector3);
				((Vector3)(ref pos))..ctor(byteReader.ReadFloat(), byteReader.ReadFloat(), byteReader.ReadFloat());
				float yaw = byteReader.ReadFloat();
				float scale = byteReader.ReadFloat();
				if (!byteReader.Overrun)
				{
					session.ApplyPlace(active, pos, yaw, scale);
				}
				break;
			}
			}
		}

		private void Route()
		{
			if (MirrorTransport.IsHost)
			{
				MirrorTransport.Broadcast(peers, -1, w.Data, w.Length);
			}
			else
			{
				MirrorTransport.SendToServer(w.Data, w.Length);
			}
		}

		public void SendPoints(byte flags, int radius, Color32 c, short[] xs, short[] ys, int count)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				w.Reset();
				w.WriteByte(16);
				w.WriteByte(flags);
				w.WriteByte((byte)radius);
				w.WriteByte(c.r);
				w.WriteByte(c.g);
				w.WriteByte(c.b);
				w.WriteByte((byte)count);
				for (int i = 0; i < count; i++)
				{
					w.WriteShort(xs[i]);
					w.WriteShort(ys[i]);
				}
				Route();
			}
		}

		public void SendShape(byte tool, int radius, Color32 c, bool filled, int x0, int y0, int x1, int y1)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				w.Reset();
				w.WriteByte(17);
				w.WriteByte(tool);
				w.WriteByte((byte)radius);
				w.WriteByte(c.r);
				w.WriteByte(c.g);
				w.WriteByte(c.b);
				w.WriteByte((byte)(filled ? 1 : 0));
				w.WriteShort((short)x0);
				w.WriteShort((short)y0);
				w.WriteShort((short)x1);
				w.WriteShort((short)y1);
				Route();
			}
		}

		public void SendFill(int x, int y, Color32 c)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				w.Reset();
				w.WriteByte(18);
				w.WriteShort((short)x);
				w.WriteShort((short)y);
				w.WriteByte(c.r);
				w.WriteByte(c.g);
				w.WriteByte(c.b);
				Route();
			}
		}

		public void SendUndo()
		{
			if (Ready)
			{
				w.Reset();
				w.WriteByte(19);
				Route();
			}
		}

		public void SendWipe()
		{
			if (Ready)
			{
				w.Reset();
				w.WriteByte(20);
				Route();
			}
		}

		public void SendPlace(bool active, Vector3 pos, float yaw, float scale)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (Ready)
			{
				BuildPlace(active, pos, yaw, scale);
				Route();
			}
		}

		private void BuildPlace(bool active, Vector3 pos, float yaw, float scale)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			w.Reset();
			w.WriteByte(21);
			w.WriteByte((byte)(active ? 1 : 0));
			w.WriteFloat(pos.x);
			w.WriteFloat(pos.y);
			w.WriteFloat(pos.z);
			w.WriteFloat(yaw);
			w.WriteFloat(scale);
		}

		private void Fail(string where, Exception e)
		{
			if (errs++ < 5)
			{
				Plugin.Log.LogError((object)("Whiteboard net (" + where + "): " + e));
			}
		}
	}
}