Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimChalkboard v0.6.1
BepInEx/plugins/ValheimWhiteboard/ValheimWhiteboard.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimWhiteboard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+de62ed66902d08e8f73cb46c27b9c93af2984742")] [assembly: AssemblyProduct("ValheimWhiteboard")] [assembly: AssemblyTitle("ValheimWhiteboard")] [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 ValheimWhiteboard { public sealed class AutosaveDraft { public byte[] Pixels; public byte[] Baseline; public int Revision; public int Failures; public bool Pending; public bool Dirty; public bool Closing; public float SubmittedAt; public float ChangedAt; private byte[] submitted; private int generation; private int submittedGeneration; public AutosaveDraft(byte[] pixels, int revision) { Pixels = (byte[])pixels.Clone(); Baseline = (byte[])pixels.Clone(); Revision = revision; } public void Changed(float now) { Dirty = true; generation++; Failures = 0; ChangedAt = now; } public bool ShouldSubmit(float now, bool drawing) { if (!Pending && Dirty && Failures < 3) { if (!Closing) { return now - SubmittedAt >= 0.1f; } return true; } return false; } public bool BeginSubmit(byte[] latest, int revision, float now) { if (Pending || !Dirty) { return false; } Revision = revision; submitted = (byte[])Pixels.Clone(); submittedGeneration = generation; Pending = true; SubmittedAt = now; return true; } public bool Observe(byte[] latest, int revision) { if (Dirty || Pending || revision <= Revision) { return false; } Pixels = (byte[])latest.Clone(); Baseline = (byte[])latest.Clone(); Revision = revision; return true; } public bool RequestClose() { if (!Dirty && !Pending) { return true; } Closing = true; Failures = 0; return false; } public bool Acknowledge(bool accepted, int revision, float now) { if (!Pending) { return false; } Pending = false; if (accepted) { Revision = revision; Baseline = submitted; Dirty = generation != submittedGeneration; Failures = 0; if (Closing) { return !Dirty; } return false; } Failures++; Closing = false; ChangedAt = now; return false; } public bool CheckTimeout(float now) { if (!Pending || now - SubmittedAt <= 10f) { return false; } Pending = false; Closing = false; Failures = 3; return true; } } public static class BoardAccess { private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic; private static readonly FieldInfo Wards = typeof(PrivateArea).GetField("m_allAreas", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo Enabled = typeof(PrivateArea).GetMethod("IsEnabled", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo Inside = typeof(PrivateArea).GetMethod("IsInside", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo Permitted = typeof(PrivateArea).GetMethod("IsPermitted", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); public static bool CanWrite(long sender, Vector3 position) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null && !((ZDOID)(ref peer.m_characterID)).Equals(ZDOID.None)) { ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO == null || zDO.GetPrefab() != StringExtensionMethods.GetStableHashCode("Player") || zDO.GetOwner() != sender || zDO.GetBool(ZDOVars.s_dead, false) || Vector3.Distance(zDO.GetPosition(), position) > 5f) { return false; } long num = zDO.GetLong(ZDOVars.s_playerID, 0L); if (num != 0L) { return WardAccess(num, position); } return false; } foreach (Player allPlayer in Player.GetAllPlayers()) { ZNetView component = ((Component)allPlayer).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO().GetOwner() == sender && !((Character)allPlayer).IsDead() && Vector3.Distance(((Component)allPlayer).transform.position, position) <= 5f && WardAccess(allPlayer.GetPlayerID(), position)) { return true; } } return false; } public static bool WardAccess(long playerId, Vector3 position) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (Wards == null || Enabled == null || Inside == null || Permitted == null) { return false; } try { bool flag = false; foreach (PrivateArea item in (List<PrivateArea>)Wards.GetValue(null)) { if (!((Object)(object)item == (Object)null) && (bool)Enabled.Invoke(item, null) && (bool)Inside.Invoke(item, new object[2] { position, 0f })) { flag = true; Piece component = ((Component)item).GetComponent<Piece>(); if (((Object)(object)component != (Object)null && component.GetCreator() == playerId) || (bool)Permitted.Invoke(item, new object[1] { playerId })) { return true; } } } return !flag; } catch { return false; } } } public static class BoardData { public const int Width = 128; public const int Height = 72; public const int ByteCount = 4608; public const int Colors = 8; public static bool TryDecode(string encoded, out byte[] pixels) { pixels = null; if (encoded == null || encoded.Length != 6144) { return false; } try { pixels = Convert.FromBase64String(encoded); } catch (FormatException) { return false; } if (pixels.Length != 4608) { return false; } byte[] array = pixels; foreach (byte b in array) { if ((b & 0xF) >= 8 || b >> 4 >= 8) { return false; } } return true; } public static byte Pixel(byte[] pixels, int x, int y) { int num = y * 128 + x; return (byte)((pixels[num / 2] >> (num & 1) * 4) & 0xF); } public static void SetPixel(byte[] pixels, int x, int y, byte color) { if (x >= 0 && y >= 0 && x < 128 && y < 72 && color < 8) { int num = y * 128 + x; int num2 = (num & 1) * 4; pixels[num / 2] = (byte)((pixels[num / 2] & ~(15 << num2)) | (color << num2)); } } public static byte[] MergeDraft(byte[] saved, byte[] baseline, byte[] draft) { byte[] array = (byte[])saved.Clone(); for (int i = 0; i < 72; i++) { for (int j = 0; j < 128; j++) { if (Pixel(draft, j, i) != Pixel(baseline, j, i)) { SetPixel(array, j, i, Pixel(draft, j, i)); } } } return array; } } public static class BoardMigration { public static bool Ready { get; private set; } public static bool Persisted { get; private set; } public static void Reset() { Ready = false; Persisted = false; } public static void EnsurePersisted(Action persist) { EnsureReady(); if (Ready && !Persisted) { persist(); Persisted = true; } } public static int Convert(IEnumerable<ZDO> records, Action<IReadOnlyList<ZDO>> backup) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = new List<ZDO>(); HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (ZDO record in records) { if (BoardSchema.LegacyKind(record.GetPrefab()) != 0 && hashSet.Add(record.m_uid)) { list.Add(record); } } if (list.Count > 0) { backup(list); } foreach (ZDO item in list) { BoardSchema.Initialize(item, BoardSchema.LegacyKind(item.GetPrefab())); } return list.Count; } public static void EnsureReady() { if (Ready || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } FieldInfo? field = typeof(ZDOMan).GetField("m_objectsByID", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { throw new InvalidOperationException("World record index unavailable; migration cannot be verified"); } List<ZDO> list = new List<ZDO>(((field.GetValue(ZDOMan.instance) as Dictionary<ZDOID, ZDO>) ?? throw new InvalidOperationException("Unexpected world record index")).Values); int num = Convert(list, MigrationBackup.Write); int num2 = 0; foreach (ZDO item in list) { if (BoardSchema.Kind(item) != 0) { BoardNetwork.Track(item); num2++; } } Ready = true; Persisted = num == 0; WhiteboardPlugin.LogInfo("Vanilla sign migration complete: " + num + " legacy boards converted; " + num2 + " chalkboard signs tracked."); } } public static class BoardNetwork { private sealed class HostState { public int Kind; public int Revision; public string Pixels; public HostState(ZDO record) { Kind = BoardSchema.Kind(record); Revision = Math.Max(0, record.GetInt("whiteboard_revision_v1", 0)); Pixels = record.GetString("whiteboard_pixels_v1", ""); if (!BoardData.TryDecode(Pixels, out var _)) { Pixels = BoardSchema.Blank; } } } public const int Protocol = 3; public const string Hello = "ChalkboardV2_Hello"; public const string HelloResult = "ChalkboardV2_HelloResult"; public const string Save = "ChalkboardV2_Save"; public const string Result = "ChalkboardV2_Result"; public const string Pixel = "ChalkboardV3_Pixel"; public const string PixelResult = "ChalkboardV3_PixelResult"; private static readonly HashSet<ZDOID> changed = new HashSet<ZDOID>(); private static readonly Dictionary<ZDOID, HostState> canonical = new Dictionary<ZDOID, HostState>(); private static readonly Dictionary<long, ZNetPeer> capable = new Dictionary<long, ZNetPeer>(); private static readonly Dictionary<ZDOID, DrawingBoard> boards = new Dictionary<ZDOID, DrawingBoard>(); private static ZRoutedRpc router; private static bool handshake; private static float nextHello; private static float nextReconcile; private static int sequence; public static bool Ready { get { if ((Object)(object)ZNet.instance != (Object)null) { if (!ZNet.instance.IsServer()) { return handshake; } return BoardMigration.Ready; } return false; } } public static long Host { get { if (!ZNet.instance.IsServer()) { return ZNet.instance.GetServerPeer()?.m_uid ?? 0; } return ZNet.GetUID(); } } public static void Reset() { canonical.Clear(); changed.Clear(); capable.Clear(); boards.Clear(); router = null; handshake = false; nextHello = 0f; nextReconcile = 0f; sequence = 0; BoardMigration.Reset(); } public static void Register() { if (ZRoutedRpc.instance != null && router != ZRoutedRpc.instance) { router = ZRoutedRpc.instance; router.Register<ZDOID, int, int, int>("ChalkboardV3_Pixel", (Method<ZDOID, int, int, int>)ReceivePixel); router.Register<ZDOID, int, bool, int, string>("ChalkboardV3_PixelResult", (Method<ZDOID, int, bool, int, string>)ReceivePixelResult); router.Register<int>("ChalkboardV2_Hello", (Action<long, int>)ReceiveHello); router.Register<int>("ChalkboardV2_HelloResult", (Action<long, int>)ReceiveHelloResult); router.Register<ZDOID, int, int, string>("ChalkboardV2_Save", (Method<ZDOID, int, int, string>)ReceiveSave); router.Register<ZDOID, int, bool, int, string>("ChalkboardV2_Result", (Method<ZDOID, int, bool, int, string>)ReceiveResult); } } public static void Tick() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null) { return; } Register(); if (ZNet.instance.IsServer()) { BoardMigration.EnsureReady(); if (Time.unscaledTime < nextReconcile) { return; } nextReconcile = Time.unscaledTime + 0.1f; foreach (ZDOID item in changed) { ZDOMan.instance.ForceSendZDO(item); } changed.Clear(); Reconcile(); } else if (!handshake && Host != 0L && Time.unscaledTime >= nextHello) { nextHello = Time.unscaledTime + 2f; router.InvokeRoutedRPC(Host, "ChalkboardV2_Hello", new object[1] { 3 }); } } public static void Reconcile() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } foreach (ZDOID item in new List<ZDOID>(canonical.Keys)) { ZDO zDO = ZDOMan.instance.GetZDO(item); if (zDO == null) { canonical.Remove(item); } else { RestoreCanonical(zDO); } } } private static bool Connected(long sender) { if (sender != ZNet.GetUID() || !ZNet.instance.IsServer()) { return ZNet.instance.GetPeer(sender) != null; } return true; } private static void ReceiveHello(long sender, int protocol) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && Connected(sender)) { if (protocol == 3) { capable[sender] = ZNet.instance.GetPeer(sender); } else { capable.Remove(sender); } router.InvokeRoutedRPC(sender, "ChalkboardV2_HelloResult", new object[1] { (protocol == 3) ? 3 : 0 }); } } private static void ReceiveHelloResult(long sender, int protocol) { if ((Object)(object)ZNet.instance != (Object)null && sender == Host) { handshake = protocol == 3; } } public static void Track(ZDO record) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && BoardSchema.Kind(record) != 0 && !canonical.ContainsKey(record.m_uid)) { canonical.Add(record.m_uid, new HostState(record)); } } public static void RestoreCanonical(ZDO record) { //IL_0022: 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) if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && record != null && canonical.TryGetValue(record.m_uid, out var value) && (record.GetPrefab() != BoardSchema.SignHash || BoardSchema.Kind(record) != value.Kind || record.GetInt("whiteboard_revision_v1", 0) != value.Revision || !(record.GetString("whiteboard_pixels_v1", "") == value.Pixels))) { record.SetOwner(ZNet.GetUID()); Apply(record, value); ZDOMan.instance.ForceSendZDO(record.m_uid); } } private static void Apply(ZDO record, HostState state) { record.SetPrefab(BoardSchema.SignHash); record.Set("chalkboard_schema", 2); record.Set("chalkboard_kind_v2", state.Kind); record.Set("whiteboard_pixels_v1", state.Pixels); record.Set("whiteboard_revision_v1", state.Revision); } public static int NextRequest() { return ++sequence; } public static void Submit(ZDOID id, int request, int revision, string pixels) { //IL_0056: 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) Register(); if (Ready && Host != 0L) { if (ZNet.instance.IsServer()) { capable[ZNet.GetUID()] = null; ReceiveSave(ZNet.GetUID(), id, request, revision, pixels); return; } router.InvokeRoutedRPC(Host, "ChalkboardV2_Save", new object[4] { id, request, revision, pixels }); } } private static void ReceiveSave(long sender, ZDOID id, int request, int expectedRevision, string encoded) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !BoardMigration.Ready || !Connected(sender) || !capable.TryGetValue(sender, out var value) || (sender != ZNet.GetUID() && value != ZNet.instance.GetPeer(sender))) { return; } ZDO zDO = ZDOMan.instance.GetZDO(id); if (zDO != null) { RestoreCanonical(zDO); } if (zDO == null || BoardSchema.Kind(zDO) == 0) { router.InvokeRoutedRPC(sender, "ChalkboardV2_Result", new object[5] { id, request, false, 0, BoardSchema.Blank }); return; } Track(zDO); HostState hostState = canonical[id]; byte[] pixels; bool flag = hostState.Revision < int.MaxValue && BoardData.TryDecode(encoded, out pixels) && BoardAccess.CanWrite(sender, zDO.GetPosition()); if (flag) { hostState.Pixels = encoded; hostState.Revision++; zDO.SetOwner(ZNet.GetUID()); Apply(zDO, hostState); ZDOMan.instance.ForceSendZDO(id); } router.InvokeRoutedRPC(sender, "ChalkboardV2_Result", new object[5] { id, request, flag, hostState.Revision, hostState.Pixels }); } public static void SubmitPixel(ZDOID id, int request, int index, int color) { //IL_0056: 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) Register(); if (Ready && Host != 0L) { if (ZNet.instance.IsServer()) { capable[ZNet.GetUID()] = null; ReceivePixel(ZNet.GetUID(), id, request, index, color); return; } router.InvokeRoutedRPC(Host, "ChalkboardV3_Pixel", new object[4] { id, request, index, color }); } } private static void ReceivePixel(long sender, ZDOID id, int request, int index, int color) { //IL_0053: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !BoardMigration.Ready || !Connected(sender) || !capable.TryGetValue(sender, out var value) || (sender != ZNet.GetUID() && value != ZNet.instance.GetPeer(sender))) { return; } ZDO zDO = ZDOMan.instance.GetZDO(id); if (zDO != null) { RestoreCanonical(zDO); } if (zDO == null || BoardSchema.Kind(zDO) == 0 || index < 0 || index >= 9216 || color < 0 || color >= 8 || !BoardAccess.CanWrite(sender, zDO.GetPosition())) { router.InvokeRoutedRPC(sender, "ChalkboardV3_PixelResult", new object[5] { id, request, false, 0, "" }); return; } RestoreCanonical(zDO); Track(zDO); HostState hostState = canonical[id]; if (hostState.Revision == int.MaxValue) { router.InvokeRoutedRPC(sender, "ChalkboardV3_PixelResult", new object[5] { id, request, false, hostState.Revision, "" }); return; } byte[] array = Convert.FromBase64String(hostState.Pixels); BoardData.SetPixel(array, index % 128, index / 128, (byte)color); hostState.Pixels = Convert.ToBase64String(array); hostState.Revision++; zDO.SetOwner(ZNet.GetUID()); Apply(zDO, hostState); changed.Add(id); string text = index + ":" + color; foreach (long item in new List<long>(capable.Keys)) { if (Connected(item) && (item == ZNet.GetUID() || capable[item] == ZNet.instance.GetPeer(item))) { router.InvokeRoutedRPC(item, "ChalkboardV3_PixelResult", new object[5] { id, (item == sender) ? request : 0, true, hostState.Revision, text }); } } } private static void ReceivePixelResult(long sender, ZDOID id, int request, bool accepted, int revision, string delta) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || sender != Host || revision < 0) { return; } int result = 0; int result2 = 0; if (accepted) { string[] array = delta.Split(':'); if (array.Length != 2 || !int.TryParse(array[0], out result) || !int.TryParse(array[1], out result2) || result < 0 || result >= 9216 || result2 < 0 || result2 >= 8) { return; } } if (boards.TryGetValue(id, out var value) && (Object)(object)value != (Object)null) { value.PixelAcknowledged(request, accepted, revision, result, (byte)result2); } } public static void Attach(DrawingBoard board, ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) boards[id] = board; } public static void Detach(DrawingBoard board, ZDOID id) { //IL_0005: 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) if (boards.TryGetValue(id, out var value) && (Object)(object)value == (Object)(object)board) { boards.Remove(id); } } private static void ReceiveResult(long sender, ZDOID id, int request, bool accepted, int revision, string snapshot) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && sender == Host && BoardData.TryDecode(snapshot, out var _) && revision >= 0 && boards.TryGetValue(id, out var value) && (Object)(object)value != (Object)null) { value.Acknowledge(request, accepted, revision, snapshot); } } } public sealed class BoardTemplate : MonoBehaviour { public int Kind; } [HarmonyPatch] public static class BoardPatches { [HarmonyPatch(typeof(ZNet), "Awake")] [HarmonyPrefix] public static void SessionStarting() { BoardNetwork.Reset(); } [HarmonyPatch(typeof(ZNet), "Awake")] [HarmonyPostfix] public static void SessionStarted() { BoardNetwork.Register(); } [HarmonyPatch(typeof(ZNetView), "GetPrefabName")] [HarmonyPostfix] public static void NetworkPrefab(ZNetView __instance, ref string __result) { BoardTemplate component = ((Component)__instance).GetComponent<BoardTemplate>(); if ((Object)(object)component != (Object)null && BoardSchema.ValidKind(component.Kind)) { __result = "sign"; } } [HarmonyPatch(typeof(ZNetView), "Awake")] [HarmonyPostfix] public static void InitializeBacking(ZNetView __instance) { BoardTemplate component = ((Component)__instance).GetComponent<BoardTemplate>(); if ((Object)(object)component != (Object)null && BoardSchema.ValidKind(component.Kind) && __instance.IsValid()) { BoardSchema.Initialize(__instance.GetZDO(), component.Kind); BoardNetwork.Track(__instance.GetZDO()); } } [HarmonyPatch(typeof(Sign), "Awake")] [HarmonyPostfix] public static void ObserveSign(Sign __instance) { if ((Object)(object)((Component)__instance).GetComponent<SignDecoration>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<SignDecoration>(); } } [HarmonyPatch(typeof(Sign), "Interact")] [HarmonyPrefix] public static bool Interact(Sign __instance, Humanoid __0, bool hold, bool alt, ref bool __result) { DrawingBoard component = ((Component)__instance).GetComponent<DrawingBoard>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.Interact(__0, hold, alt); return false; } [HarmonyPatch(typeof(Sign), "GetHoverText")] [HarmonyPrefix] public static bool HoverText(Sign __instance, ref string __result) { DrawingBoard component = ((Component)__instance).GetComponent<DrawingBoard>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.GetHoverText(); return false; } [HarmonyPatch(typeof(Sign), "GetHoverName")] [HarmonyPrefix] public static bool HoverName(Sign __instance, ref string __result) { DrawingBoard component = ((Component)__instance).GetComponent<DrawingBoard>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.GetHoverName(); return false; } [HarmonyPatch(typeof(ZNet), "ServerLoadWorld")] [HarmonyPostfix] public static void WorldLoaded() { try { BoardMigration.EnsureReady(); } catch (Exception ex) { WhiteboardPlugin.LogError("Migration failed: " + ex); } } [HarmonyPatch(typeof(ZNet), "OpenServer")] [HarmonyPrefix] public static bool BeforeOpen() { try { BoardMigration.EnsurePersisted(MigrationPersistence.Save); return BoardMigration.Ready && BoardMigration.Persisted; } catch (Exception ex) { WhiteboardPlugin.LogError("Migration failed; server remains closed: " + ex); return false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] public static bool BeforePeerInfo() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return true; } try { BoardMigration.EnsurePersisted(MigrationPersistence.Save); return BoardMigration.Ready && BoardMigration.Persisted; } catch (Exception ex) { WhiteboardPlugin.LogError("Migration failed; connection blocked: " + ex); return false; } } [HarmonyPatch(typeof(ZDO), "Deserialize")] [HarmonyPostfix] public static void IncomingRecord(ZDO __instance) { BoardNetwork.RestoreCanonical(__instance); } [HarmonyPatch(typeof(ZDOMan), "PrepareSave")] [HarmonyPrefix] public static void BeforeSaveSnapshot() { BoardNetwork.Reconcile(); } } public static class BoardSchema { public const string KindKey = "chalkboard_kind_v2"; public const string SchemaKey = "chalkboard_schema"; public const string DataKey = "whiteboard_pixels_v1"; public const string RevisionKey = "whiteboard_revision_v1"; public const int Version = 2; public static readonly int SignHash = StringExtensionMethods.GetStableHashCode("sign"); public static readonly string[] LegacyNames = new string[3] { "piece_whiteboard", "piece_whiteboard_medium", "piece_whiteboard_large" }; public static readonly string Blank = Convert.ToBase64String(new byte[4608]); public static bool ValidKind(int kind) { if (kind >= 1) { return kind <= 3; } return false; } public static int Kind(ZDO zdo) { if (zdo == null || zdo.GetPrefab() != SignHash || zdo.GetInt("chalkboard_schema", 0) != 2 || !ValidKind(zdo.GetInt("chalkboard_kind_v2", 0))) { return 0; } return zdo.GetInt("chalkboard_kind_v2", 0); } public static float Scale(int kind) { return kind switch { 3 => 2f, 2 => 1.5f, _ => 1f, }; } public static string Name(int kind) { return "Chalkboard (" + kind switch { 3 => "Large", 2 => "Medium", _ => "Small", } + ")"; } public static int LegacyKind(int prefab) { for (int i = 0; i < LegacyNames.Length; i++) { if (prefab == StringExtensionMethods.GetStableHashCode(LegacyNames[i])) { return i + 1; } } return 0; } public static void Initialize(ZDO zdo, int kind) { if (zdo == null || !ValidKind(kind)) { throw new ArgumentException("Invalid chalkboard record"); } if (zdo.GetPrefab() != SignHash && LegacyKind(zdo.GetPrefab()) == 0) { throw new InvalidOperationException("Not a sign or legacy board"); } zdo.SetPrefab(SignHash); zdo.Set("chalkboard_schema", 2); zdo.Set("chalkboard_kind_v2", kind); if (string.IsNullOrEmpty(zdo.GetString(ZDOVars.s_text, ""))) { zdo.Set(ZDOVars.s_text, "Chalkboard"); } if (!BoardData.TryDecode(zdo.GetString("whiteboard_pixels_v1", ""), out var _)) { zdo.Set("whiteboard_pixels_v1", Blank); } } } public sealed class DrawingBoard : MonoBehaviour, Hoverable, Interactable { public const int Width = 128; public const int Height = 72; public const int ByteCount = 4608; public static readonly Color32[] Palette = (Color32[])(object)new Color32[8] { new Color32((byte)29, (byte)42, (byte)36, byte.MaxValue), new Color32((byte)235, (byte)231, (byte)211, byte.MaxValue), new Color32((byte)219, (byte)133, (byte)121, byte.MaxValue), new Color32((byte)139, (byte)180, (byte)203, byte.MaxValue), new Color32((byte)160, (byte)191, (byte)139, byte.MaxValue), new Color32((byte)229, (byte)210, (byte)138, byte.MaxValue), new Color32((byte)188, (byte)161, (byte)200, byte.MaxValue), new Color32((byte)225, (byte)176, (byte)125, byte.MaxValue) }; public string DisplayName = "Chalkboard"; private Transform surface; private Material surfaceMaterial; private Material material; private ZNetView view; private Texture2D texture; private Mesh drawingMesh; private int seenRevision = -1; private int replyRevision = -1; private int awaiting; private string replyPixels; private float nextPoll; private ZDOID id; public int Revision { get { if (!((Object)(object)view != (Object)null) || !view.IsValid()) { return 0; } return Math.Max(replyRevision, view.GetZDO().GetInt("whiteboard_revision_v1", 0)); } } private void Awake() { view = ((Component)this).GetComponent<ZNetView>(); } public void Configure(Transform target, Material template, string name) { surface = target; surfaceMaterial = template; DisplayName = name; } private void Start() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)view == (Object)null) && view.IsValid()) { id = view.GetZDO().m_uid; BoardNetwork.Attach(this, id); if ((Object)(object)surface != (Object)null && (Object)(object)surfaceMaterial != (Object)null) { texture = NewTexture(); material = new Material(surfaceMaterial) { mainTexture = (Texture)(object)texture }; GameObject val = new GameObject("Drawing"); val.layer = ((Component)this).gameObject.layer; val.transform.SetParent(surface, false); val.transform.localPosition = Vector3.zero; drawingMesh = new Mesh(); drawingMesh.vertices = (Vector3[])(object)new Vector3[4] { new Vector3(-1f, -0.575f, 0f), new Vector3(1f, -0.575f, 0f), new Vector3(1f, 0.575f, 0f), new Vector3(-1f, 0.575f, 0f) }; drawingMesh.uv = (Vector2[])(object)new Vector2[4] { new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(0f, 1f) }; drawingMesh.colors = (Color[])(object)new Color[4] { Color.white, Color.white, Color.white, Color.white }; drawingMesh.triangles = new int[6] { 0, 2, 1, 0, 3, 2 }; drawingMesh.RecalculateNormals(); val.AddComponent<MeshFilter>().sharedMesh = drawingMesh; ((Renderer)val.AddComponent<MeshRenderer>()).sharedMaterial = material; } Refresh(); } } private void Update() { if (Time.unscaledTime >= nextPoll) { nextPoll = Time.unscaledTime + 0.1f; Refresh(); } } private void Refresh() { if ((Object)(object)view == (Object)null || !view.IsValid()) { return; } int revision = Revision; if (revision != seenRevision) { seenRevision = revision; if ((Object)(object)texture != (Object)null) { PaintTexture(texture, ReadPixels()); } } } public byte[] ReadPixels() { if ((Object)(object)view == (Object)null || !view.IsValid()) { return new byte[4608]; } ZDO zDO = view.GetZDO(); if (!TryDecode((replyRevision >= zDO.GetInt("whiteboard_revision_v1", 0)) ? replyPixels : zDO.GetString("whiteboard_pixels_v1", ""), out var pixels)) { return new byte[4608]; } return pixels; } public bool CanEdit() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (BoardNetwork.Ready && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && (Object)(object)view != (Object)null && view.IsValid() && BoardSchema.Kind(view.GetZDO()) != 0 && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)this).transform.position) <= 5f) { return PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false); } return false; } public float GetHoverOffset() { return 0f; } public string GetHoverName() { return DisplayName; } public string GetHoverText() { return Localization.instance.Localize(DisplayName + (BoardNetwork.Ready ? "\n[<color=yellow><b>$KEY_Use</b></color>] Draw" : "\nConnecting to chalkboard host…")); } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold || !CanEdit()) { return false; } DrawingEditor.Open(this); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } public void Submit(int baseRevision, byte[] pixels) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (!CanEdit()) { DrawingEditor.Report(this, accepted: false, Revision); return; } awaiting = BoardNetwork.NextRequest(); BoardNetwork.Submit(id, awaiting, baseRevision, Convert.ToBase64String(pixels)); } public void Acknowledge(int request, bool accepted, int revision, string snapshot) { if (request == awaiting && TryDecode(snapshot, out var _) && revision >= replyRevision) { replyRevision = revision; replyPixels = snapshot; Refresh(); DrawingEditor.Report(this, accepted, revision); } } public void SubmitPixel(PixelDraft.Edit edit) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!CanEdit()) { DrawingEditor.ReportPixel(this, edit.Request, accepted: false); } else { BoardNetwork.SubmitPixel(id, edit.Request, edit.Index, edit.Color); } } public void PixelAcknowledged(int request, bool accepted, int revision, int index, byte color) { if (accepted && revision == Revision + 1) { byte[] array = ReadPixels(); BoardData.SetPixel(array, index % 128, index / 128, color); replyRevision = revision; replyPixels = Convert.ToBase64String(array); Refresh(); } DrawingEditor.ReportPixel(this, request, accepted); } public static bool TryDecode(string data, out byte[] pixels) { return BoardData.TryDecode(data, out pixels); } public static byte Pixel(byte[] pixels, int x, int y) { return BoardData.Pixel(pixels, x, y); } public static void SetPixel(byte[] pixels, int x, int y, byte color) { BoardData.SetPixel(pixels, x, y, color); } public static byte[] MergeDraft(byte[] saved, byte[] baseline, byte[] draft) { return BoardData.MergeDraft(saved, baseline, draft); } public static Texture2D NewTexture() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new Texture2D(128, 72, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; } public static void PaintTexture(Texture2D target, byte[] pixels) { //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) Color32[] array = (Color32[])(object)new Color32[9216]; for (int i = 0; i < 72; i++) { for (int j = 0; j < 128; j++) { array[(71 - i) * 128 + j] = Palette[Pixel(pixels, j, i)]; } } target.SetPixels32(array); target.Apply(false); } private void OnDestroy() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) BoardNetwork.Detach(this, id); DrawingEditor.BoardDestroyed(this); if ((Object)(object)texture != (Object)null) { Object.Destroy((Object)(object)texture); } if ((Object)(object)material != (Object)null) { Object.Destroy((Object)(object)material); } if ((Object)(object)drawingMesh != (Object)null) { Object.Destroy((Object)(object)drawingMesh); } } } public sealed class DrawingEditor : MonoBehaviour { private static DrawingEditor instance; private DrawingBoard board; private Texture2D preview; private PixelDraft draft; private readonly Stack<byte[]> undo = new Stack<byte[]>(); private int radius = 1; private byte color = 1; private bool drawing; private bool inputBlocked; private Vector2Int previous; private CursorLockMode oldLock; private bool oldVisible; private string status = "Your marks are kept automatically."; private GameObject uiRoot; private RectTransform panel; private RectTransform slate; private RectTransform tools; private RectTransform frame; private Text statusText; private Text toolHint; private readonly List<Button> editingButtons = new List<Button>(); private readonly List<Outline> chalkSelection = new List<Outline>(); private readonly List<Outline> sizeSelection = new List<Outline>(); private Button retry; private static readonly Color Gold = new Color(0.91f, 0.72f, 0.34f, 1f); private static readonly Color Parchment = new Color(0.88f, 0.84f, 0.72f, 1f); private byte[] pixels => draft.Pixels; private bool pending => draft.PendingCount != 0; private bool dirty => draft.Dirty; private bool closing => draft.Closing; public static void Open(DrawingBoard target) { //IL_0013: 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_0065: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)instance != (Object)null)) { instance = new GameObject("ChalkboardEditor").AddComponent<DrawingEditor>(); instance.board = target; instance.draft = new PixelDraft(target.ReadPixels()); instance.preview = DrawingBoard.NewTexture(); instance.Refresh(); instance.oldLock = Cursor.lockState; instance.oldVisible = Cursor.visible; try { instance.CreateUI(); } catch { Object.Destroy((Object)(object)((Component)instance).gameObject); throw; } GUIManager.BlockInput(true); instance.inputBlocked = true; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } private RectTransform Rect(GameObject obj, Vector2 min, Vector2 max, Vector2 position, Vector2 size) { //IL_0007: 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_001f: 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_0032: Unknown result type (might be due to invalid IL or missing references) RectTransform component = obj.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = position; component.sizeDelta = size; return component; } private GameObject Fill(string name, Transform parent, Color tint) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform obj = Rect(val, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero); obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; ((Graphic)val.GetComponent<Image>()).color = tint; return val; } private Text TextAt(string value, Transform parent, float x, float y, float width, float height, int fontSize = 20, bool heading = false) { //IL_0013: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) GUIManager val = GUIManager.Instance; Text component = val.CreateText(value, parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), heading ? val.NorseBold : val.AveriaSerif, fontSize, heading ? Gold : Parchment, false, Color.black, width, height, false).GetComponent<Text>(); component.alignment = (TextAnchor)3; ((Graphic)component).raycastTarget = false; component.horizontalOverflow = (HorizontalWrapMode)0; return component; } private Button ButtonAt(string name, Transform parent, float x, float y, float width, float height, Action action, bool editing = true) { //IL_001f: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateButton(name, parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), width, height); Button component = obj.GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { action(); }); Text componentInChildren = obj.GetComponentInChildren<Text>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.font = GUIManager.Instance.AveriaSerifBold; componentInChildren.fontSize = 19; ((Graphic)componentInChildren).color = Parchment; } if (editing) { editingButtons.Add(component); } return component; } private Outline Selection(Button button) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Outline obj = ((Component)button).gameObject.AddComponent<Outline>(); ((Shadow)obj).effectColor = Gold; ((Shadow)obj).effectDistance = new Vector2(2f, -2f); return obj; } private void CreateUI() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0618: Unknown result type (might be due to invalid IL or missing references) //IL_0627: Unknown result type (might be due to invalid IL or missing references) //IL_062c: Unknown result type (might be due to invalid IL or missing references) //IL_0675: Unknown result type (might be due to invalid IL or missing references) //IL_067c: Expected O, but got Unknown //IL_069f: Unknown result type (might be due to invalid IL or missing references) //IL_06a4: Unknown result type (might be due to invalid IL or missing references) //IL_06a9: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_0721: Unknown result type (might be due to invalid IL or missing references) //IL_0736: Unknown result type (might be due to invalid IL or missing references) //IL_074b: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Unknown result type (might be due to invalid IL or missing references) //IL_07bf: Unknown result type (might be due to invalid IL or missing references) //IL_07c6: Unknown result type (might be due to invalid IL or missing references) //IL_07d7: Unknown result type (might be due to invalid IL or missing references) uiRoot = new GameObject("ChalkboardCanvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Canvas component = uiRoot.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.overrideSorting = true; component.sortingOrder = 32760; uiRoot.GetComponent<CanvasScaler>().uiScaleMode = (ScaleMode)0; uiRoot.AddComponent<GuiScaler>(); Fill("DimmedWorld", uiRoot.transform, new Color(0.025f, 0.032f, 0.028f, 0.97f)); GameObject val = GUIManager.Instance.CreateWoodpanel(uiRoot.transform, new Vector2(0.025f, 0.025f), new Vector2(0.975f, 0.975f), Vector2.zero, 0f, 0f, false); panel = val.GetComponent<RectTransform>(); panel.offsetMin = Vector2.zero; panel.offsetMax = Vector2.zero; TextAt("CHALKBOARD", (Transform)(object)panel, 180f, 42f, 290f, 52f, 38, heading: true); TextAt(board.DisplayName + " / Sketches & plans", (Transform)(object)panel, 490f, 42f, 300f, 40f, 18); RectTransform component2 = ((Component)ButtonAt("Done [Esc]", (Transform)(object)panel, 0f, 42f, 154f, 42f, RequestClose, editing: false)).GetComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(1f, 1f); component2.anchorMax = val2; component2.anchorMin = val2; component2.anchoredPosition = new Vector2(-112f, -42f); GameObject obj = Fill("HeaderRule", (Transform)(object)panel, new Color(0.61f, 0.43f, 0.2f, 0.7f)); Rect(obj, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, -79f), new Vector2(-56f, 2f)); tools = new GameObject("ChalkTools", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>(); ((Transform)tools).SetParent((Transform)(object)panel, false); Rect(((Component)tools).gameObject, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-125f, -96f), new Vector2(190f, 360f)); tools.pivot = new Vector2(0.5f, 1f); TextAt("CHALK", (Transform)(object)tools, 94f, 20f, 174f, 32f, 27, heading: true); for (byte b = 1; b < DrawingBoard.Palette.Length; b++) { byte selected = b; int num = b - 1; Button val3 = ButtonAt("", (Transform)(object)tools, 28 + num % 4 * 45, 65 + num / 4 * 50, 38f, 38f, delegate { color = selected; }); GameObject val4 = Fill("ChalkStick", ((Component)val3).transform, Color32.op_Implicit(DrawingBoard.Palette[b])); Rect(val4, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(23f, 7f)); val4.transform.localRotation = Quaternion.Euler(0f, 0f, -12f); ((Graphic)val4.GetComponent<Image>()).raycastTarget = false; chalkSelection.Add(Selection(val3)); } Button val5 = ButtonAt("Eraser", (Transform)(object)tools, 160f, 115f, 38f, 38f, delegate { color = 0; }); Text componentInChildren = ((Component)val5).GetComponentInChildren<Text>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.fontSize = 12; } chalkSelection.Insert(0, Selection(val5)); TextAt("STROKE", (Transform)(object)tools, 94f, 169f, 174f, 32f, 25, heading: true); for (int num2 = 1; num2 <= 6; num2++) { int selected2 = num2; Button val6 = ButtonAt(num2.ToString(), (Transform)(object)tools, 18 + (num2 - 1) * 30, 213f, 26f, 36f, delegate { radius = selected2; }); Text componentInChildren2 = ((Component)val6).GetComponentInChildren<Text>(); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.fontSize = 16; } sizeSelection.Add(Selection(val6)); } ButtonAt("Undo stroke", (Transform)(object)tools, 94f, 273f, 174f, 42f, delegate { if (undo.Count > 0) { draft.Replace(undo.Pop()); Changed(); } }); ButtonAt("Wipe board", (Transform)(object)tools, 94f, 325f, 174f, 42f, delegate { Snapshot(); draft.Replace(new byte[4608], all: true); Changed(); }); toolHint = TextAt("Draw with the mouse.\nMarks sync as you draw.", (Transform)(object)tools, 94f, 392f, 174f, 68f, 18); GameObject val7 = GUIManager.Instance.CreateWoodpanel((Transform)(object)panel, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, 100f, 100f, false); frame = val7.GetComponent<RectTransform>(); GameObject val8 = new GameObject("Slate", new Type[2] { typeof(RectTransform), typeof(RawImage) }); val8.transform.SetParent((Transform)(object)frame, false); slate = val8.GetComponent<RectTransform>(); Rect(val8, Vector2.zero, Vector2.one, Vector2.zero, new Vector2(-22f, -22f)); RawImage component3 = val8.GetComponent<RawImage>(); component3.texture = (Texture)(object)preview; ((Graphic)component3).raycastTarget = true; statusText = TextAt(status, (Transform)(object)panel, 0f, 0f, 100f, 38f, 18); RectTransform rectTransform = ((Graphic)statusText).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(1f, 0f); rectTransform.anchoredPosition = new Vector2(-75f, 28f); rectTransform.sizeDelta = new Vector2(-220f, 38f); retry = ButtonAt("Retry", (Transform)(object)panel, 0f, 0f, 100f, 36f, delegate { draft.Retry(); Flush(); }, editing: false); RectTransform component4 = ((Component)retry).GetComponent<RectTransform>(); ((Vector2)(ref val2))..ctor(1f, 0f); component4.anchorMax = val2; component4.anchorMin = val2; component4.anchoredPosition = new Vector2(-85f, 28f); ((Component)retry).gameObject.SetActive(false); Layout(); } private void Layout() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) Rect rect = panel.rect; Vector2 size = ((Rect)(ref rect)).size; float num = Mathf.Max(32f, size.x - 284f); float num2 = Mathf.Max(32f, size.y - 166f); float num3 = Mathf.Min(num, num2 * 16f / 9f); frame.sizeDelta = new Vector2(num3, num3 * 9f / 16f); frame.anchoredPosition = new Vector2(34f + num / 2f, -92f - num2 / 2f); statusText.text = status; ((Component)toolHint).gameObject.SetActive(size.y >= 620f); foreach (Button editingButton in editingButtons) { ((Selectable)editingButton).interactable = !closing; } for (int i = 0; i < chalkSelection.Count; i++) { ((Behaviour)chalkSelection[i]).enabled = i == color; } for (int j = 0; j < sizeSelection.Count; j++) { ((Behaviour)sizeSelection[j]).enabled = j + 1 == radius; } ((Component)retry).gameObject.SetActive(draft.Failed); } private void Update() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)board == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { Close(); return; } if (((Character)Player.m_localPlayer).IsDead() || !board.CanEdit()) { Close(); return; } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; Layout(); if (Input.GetKeyDown((KeyCode)27)) { RequestClose(); return; } if (!closing) { bool flag = RectTransformUtility.RectangleContainsScreenPoint(slate, Vector2.op_Implicit(Input.mousePosition), (Camera)null); if (Input.GetMouseButtonDown(0) && flag) { Snapshot(); drawing = true; previous = Point(); Dab(previous); Changed(); } else if (Input.GetMouseButton(0) && drawing) { if (flag) { Vector2Int b = Point(); Line(previous, b); previous = b; Changed(); } else { drawing = false; } } if (Input.GetMouseButtonUp(0)) { drawing = false; } } draft.Observe(board.ReadPixels()); Refresh(); draft.CheckTimeout(Time.unscaledTime); if (draft.Failed) { status = "Could not sync. Your marks are still here - Retry."; } Flush(); if (closing && !dirty) { Close(); } } private Vector2Int Point() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_006f: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle(slate, Vector2.op_Implicit(Input.mousePosition), (Camera)null, ref val); Rect rect = slate.rect; return new Vector2Int(Mathf.Clamp((int)((val.x - ((Rect)(ref rect)).xMin) / ((Rect)(ref rect)).width * 128f), 0, 127), Mathf.Clamp((int)((((Rect)(ref rect)).yMax - val.y) / ((Rect)(ref rect)).height * 72f), 0, 71)); } private void Flush() { if ((Object)(object)board != (Object)null) { draft.Flush(Time.unscaledTime, BoardNetwork.NextRequest, board.SubmitPixel); } } private void RequestClose() { drawing = false; draft.Closing = true; Flush(); if (!dirty) { Close(); } } private void Dab(Vector2Int point) { for (int i = -radius + 1; i < radius; i++) { for (int j = -radius + 1; j < radius; j++) { if (j * j + i * i <= radius * radius) { draft.Set(((Vector2Int)(ref point)).x + j, ((Vector2Int)(ref point)).y + i, color); } } } } private void Line(Vector2Int a, Vector2Int b) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(Mathf.Abs(((Vector2Int)(ref a)).x - ((Vector2Int)(ref b)).x), Mathf.Abs(((Vector2Int)(ref a)).y - ((Vector2Int)(ref b)).y)); for (int i = 0; i <= num; i++) { Dab(new Vector2Int(Mathf.RoundToInt(Mathf.Lerp((float)((Vector2Int)(ref a)).x, (float)((Vector2Int)(ref b)).x, (num == 0) ? 0f : ((float)i / (float)num))), Mathf.RoundToInt(Mathf.Lerp((float)((Vector2Int)(ref a)).y, (float)((Vector2Int)(ref b)).y, (num == 0) ? 0f : ((float)i / (float)num))))); } } private void Snapshot() { if (undo.Count >= 30) { undo.Clear(); } undo.Push((byte[])pixels.Clone()); } private void Changed() { status = "Drawing…"; Refresh(); } private void Refresh() { DrawingBoard.PaintTexture(preview, pixels); } public static void Report(DrawingBoard target, bool accepted, int newRevision) { } public static void ReportPixel(DrawingBoard target, int request, bool accepted) { if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.board != (Object)(object)target)) { if (request != 0) { instance.draft.Acknowledge(request, accepted); } instance.status = (instance.draft.Failed ? "Could not sync. Your marks are still here - Retry." : "Your marks are kept automatically."); } } public static void BoardDestroyed(DrawingBoard target) { if ((Object)(object)instance != (Object)null && (Object)(object)instance.board == (Object)(object)target) { instance.Close(); } } private void Close() { Object.Destroy((Object)(object)((Component)this).gameObject); } private void OnDestroy() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (draft != null && (Object)(object)board != (Object)null && board.CanEdit()) { Flush(); } if (inputBlocked) { GUIManager.BlockInput(false); } Cursor.lockState = oldLock; Cursor.visible = oldVisible; if ((Object)(object)uiRoot != (Object)null) { Object.Destroy((Object)(object)uiRoot); } if ((Object)(object)preview != (Object)null) { Object.Destroy((Object)(object)preview); } if ((Object)(object)instance == (Object)(object)this) { instance = null; } } } public static class MigrationBackup { public static void Write(IReadOnlyList<ZDO> records) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(Paths.ConfigPath, "ValheimChalkboard", "migration-backups"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff") + ".cbbackup"); using (FileStream fileStream = new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.Read)) { using BinaryWriter binaryWriter = new BinaryWriter(fileStream); binaryWriter.Write("ChalkboardMigrationBackupV2"); binaryWriter.Write(records.Count); foreach (ZDO record in records) { ZPackage val = new ZPackage(); record.Serialize(val); byte[] array = val.GetArray(); binaryWriter.Write(((object)Unsafe.As<ZDOID, ZDOID>(ref record.m_uid)/*cast due to .constrained prefix*/).ToString()); binaryWriter.Write(record.GetOwner()); Vector3 position = record.GetPosition(); binaryWriter.Write(position.x); binaryWriter.Write(position.y); binaryWriter.Write(position.z); binaryWriter.Write(array.Length); binaryWriter.Write(array); } binaryWriter.Flush(); fileStream.Flush(flushToDisk: true); } WhiteboardPlugin.LogInfo("Backed up legacy board records to " + text2); } } internal static class MigrationPersistence { internal static void Save() { uint saveNumber = SaveSystem.GetSaveNumber(); AccessTools.Method(typeof(ZNet), "SaveWorld", new Type[1] { typeof(bool) }, (Type[])null).Invoke(ZNet.instance, new object[1] { true }); if (SaveSystem.GetSaveNumber() == saveNumber) { throw new InvalidOperationException("Migrated world did not commit; server remains closed."); } WhiteboardPlugin.LogInfo("Migrated world committed before opening server."); } } public sealed class PixelDraft { public sealed class Edit { public int Request; public int Index; public byte Color; public float SentAt; } private readonly Queue<Edit> queued = new Queue<Edit>(); private readonly Dictionary<int, Edit> pending = new Dictionary<int, Edit>(); public bool Closing; public bool Failed; public byte[] Pixels { get; private set; } public bool Dirty { get { if (queued.Count == 0) { return pending.Count != 0; } return true; } } public int PendingCount => pending.Count; public PixelDraft(byte[] pixels) { Pixels = (byte[])pixels.Clone(); } public void Set(int x, int y, byte color) { if (x >= 0 && y >= 0 && x < 128 && y < 72 && color < 8 && BoardData.Pixel(Pixels, x, y) != color) { BoardData.SetPixel(Pixels, x, y, color); queued.Enqueue(new Edit { Index = y * 128 + x, Color = color }); } } public void Replace(byte[] image, bool all = false) { for (int i = 0; i < 72; i++) { for (int j = 0; j < 128; j++) { byte b = BoardData.Pixel(image, j, i); if (all && BoardData.Pixel(Pixels, j, i) == b) { queued.Enqueue(new Edit { Index = i * 128 + j, Color = b }); } else { Set(j, i, b); } } } } public void Flush(float now, Func<int> next, Action<Edit> send) { if (!Failed) { int num = 256; while (!Failed && queued.Count > 0 && pending.Count < 512 && num-- > 0) { Edit edit = queued.Dequeue(); edit.Request = next(); edit.SentAt = now; pending.Add(edit.Request, edit); send(edit); } } } public void Acknowledge(int request, bool accepted) { if (pending.TryGetValue(request, out var value)) { pending.Remove(request); if (!accepted) { queued.Enqueue(value); Failed = true; Closing = false; } } } public void Observe(byte[] authoritative) { Pixels = (byte[])authoritative.Clone(); List<Edit> list = new List<Edit>(pending.Values); list.Sort((Edit a, Edit b) => a.Request.CompareTo(b.Request)); foreach (Edit item in list) { BoardData.SetPixel(Pixels, item.Index % 128, item.Index / 128, item.Color); } foreach (Edit item2 in queued) { BoardData.SetPixel(Pixels, item2.Index % 128, item2.Index / 128, item2.Color); } } public void CheckTimeout(float now) { foreach (Edit value in pending.Values) { if (now - value.SentAt > 10f) { Failed = true; Closing = false; break; } } } public void Retry() { List<Edit> list = new List<Edit>(pending.Values); list.Sort((Edit a, Edit b) => a.Request.CompareTo(b.Request)); list.AddRange(queued); pending.Clear(); queued.Clear(); foreach (Edit item in list) { queued.Enqueue(item); } Failed = false; } } internal static class RuntimeCompatibilityTests { public static void Run(Action<bool, string> check) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Expected O, but got Unknown BepInPlugin plugin = (BepInPlugin)Attribute.GetCustomAttribute(typeof(WhiteboardPlugin), typeof(BepInPlugin)); NetworkCompatibilityAttribute val = (NetworkCompatibilityAttribute)Attribute.GetCustomAttribute(typeof(WhiteboardPlugin), typeof(NetworkCompatibilityAttribute)); Assembly assembly = typeof(Main).Assembly; Type moduleType = assembly.GetType("Jotunn.Utils.ModModule", throwOnError: true); Type dataType = assembly.GetType("Jotunn.Utils.ModuleVersionData", throwOnError: true); Type listType = typeof(List<>).MakeGenericType(moduleType); object module = AccessTools.Constructor(moduleType, new Type[2] { typeof(BepInPlugin), typeof(NetworkCompatibilityAttribute) }, false).Invoke(new object[2] { plugin, val }); check(Needed("IsNeededOnServer") && !Needed("IsNeededOnClient"), "actual Jotunn module declares host required and client optional"); MethodInfo compare = AccessTools.Method(assembly.GetType("Jotunn.Utils.ModCompatibility", throwOnError: true), "CompareVersionData", (Type[])null, (Type[])null); object obj = Data(new object[1] { module }); object obj2 = Data(Array.Empty<object>()); check(Compatible(obj, obj2), "actual Jotunn version comparison allows absent client chalkboard module"); check(!Compatible(obj2, obj), "actual Jotunn version comparison rejects missing host chalkboard module"); check(!Compatible(obj, Data(new object[1] { Version("0.4.1") })), "actual Jotunn version comparison rejects old custom-prefab clients"); check(!Compatible(obj, Data(new object[1] { Version("0.5.0") })), "actual Jotunn version comparison rejects older snapshot protocol clients"); check(Compatible(obj, Data(new object[1] { Version("0.6.1") })), "actual Jotunn minor policy allows compatible patch versions"); ZPackage val2 = (ZPackage)AccessTools.Method(dataType, "ToZPackage", (Type[])null, (Type[])null).Invoke(obj, null); val2.SetPos(0); object client = AccessTools.Constructor(dataType, new Type[1] { typeof(ZPackage) }, false).Invoke(new object[1] { val2 }); check(Compatible(obj, client), "actual Jotunn version package roundtrip preserves compatibility"); bool Compatible(object server, object obj3) { return (bool)compare.Invoke(null, new object[2] { server, obj3 }); } object Data(params object[] modules) { IList list = (IList)Activator.CreateInstance(listType); foreach (object value in modules) { list.Add(value); } return AccessTools.Constructor(dataType, new Type[1] { listType }, false).Invoke(new object[1] { list }); } bool Needed(string name) { return (bool)AccessTools.Method(moduleType, name, (Type[])null, (Type[])null).Invoke(module, null); } object Version(string version) { return AccessTools.Constructor(moduleType, new Type[5] { typeof(string), typeof(string), typeof(Version), typeof(CompatibilityLevel), typeof(VersionStrictness) }, false).Invoke(new object[5] { plugin.GUID, plugin.Name, new Version(version), (object)(CompatibilityLevel)4, (object)(VersionStrictness)2 }); } } } public static class RuntimeSmokeTests { public static string Run() { //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Expected O, but got Unknown //IL_0453: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Expected O, but got Unknown //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Expected O, but got Unknown //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04dc: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || !BoardMigration.Ready) { throw new InvalidOperationException("Start a disposable local/host world before running chalkboard_validate."); } List<string> messages = new List<string>(); List<ZDO> scratch = new List<ZDO>(); try { MethodInfo[] array = new MethodInfo[8] { AccessTools.Method(typeof(ZNetView), "GetPrefabName", (Type[])null, (Type[])null), AccessTools.Method(typeof(ZNetView), "Awake", (Type[])null, (Type[])null), AccessTools.Method(typeof(Sign), "Awake", (Type[])null, (Type[])null), AccessTools.Method(typeof(Sign), "Interact", (Type[])null, (Type[])null), AccessTools.Method(typeof(ZDO), "Deserialize", (Type[])null, (Type[])null), AccessTools.Method(typeof(ZNet), "RPC_PeerInfo", (Type[])null, (Type[])null), AccessTools.Method(typeof(ZDOMan), "PrepareSave", (Type[])null, (Type[])null), AccessTools.Method(typeof(ZNet), "ServerLoadWorld", (Type[])null, (Type[])null) }; foreach (MethodInfo methodInfo in array) { int condition; if (methodInfo != null) { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); condition = ((patchInfo != null && patchInfo.Owners.Contains("marcus.valheim.whiteboard")) ? 1 : 0); } else { condition = 0; } Check((byte)condition != 0, "Harmony hook installed: " + methodInfo?.DeclaringType?.Name + "." + methodInfo?.Name); } GameObject prefab = PrefabManager.Instance.GetPrefab("sign"); RuntimeCompatibilityTests.Run(Check); Check((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Sign>() != (Object)null && (Object)(object)prefab.GetComponent<BoardTemplate>() == (Object)null, "vanilla sign prefab remains undecorated and retains Sign"); for (int j = 1; j <= 3; j++) { GameObject prefab2 = PrefabManager.Instance.GetPrefab(BoardSchema.LegacyNames[j - 1]); Check((Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent<Sign>() != (Object)null && prefab2.GetComponent<BoardTemplate>().Kind == j, "menu template retains vanilla Sign: " + BoardSchema.Name(j)); string text = (string)AccessTools.Method(typeof(ZNetView), "GetPrefabName", (Type[])null, (Type[])null).Invoke(prefab2.GetComponent<ZNetView>(), null); Check(text == "sign", "actual patched GetPrefabName returns sign: " + j); Requirement[] resources = prefab.GetComponent<Piece>().m_resources; Requirement[] resources2 = prefab2.GetComponent<Piece>().m_resources; Check(resources2.Length == resources.Length && resources2.Zip(resources, (Requirement a, Requirement b) => a.m_amount == b.m_amount && (Object)(object)a.m_resItem == (Object)(object)b.m_resItem).All((bool x) => x), "vanilla recipe/refunds retained: " + j); Check(prefab2.GetComponentsInChildren<Collider>(true).Length == prefab.GetComponentsInChildren<Collider>(true).Length, "template solid collider count matches vanilla: " + j); } ZDO val = Record(); val.SetPrefab(StringExtensionMethods.GetStableHashCode(BoardSchema.LegacyNames[1])); byte[] array2 = new byte[4608]; BoardData.SetPixel(array2, 30, 40, 7); string text2 = Convert.ToBase64String(array2); val.Set("whiteboard_pixels_v1", text2); val.Set("whiteboard_revision_v1", 12); bool backedUp = false; BoardMigration.Convert((IEnumerable<ZDO>)(object)new ZDO[1] { val }, delegate(IReadOnlyList<ZDO> records) { backedUp = records[0].GetPrefab() == StringExtensionMethods.GetStableHashCode(BoardSchema.LegacyNames[1]); }); Check(backedUp && BoardSchema.Kind(val) == 2 && val.GetString("whiteboard_pixels_v1", "") == text2 && val.GetInt("whiteboard_revision_v1", 0) == 12, "migration preserves actual game ZDO metadata"); ZPackage val2 = new ZPackage(); val.Serialize(val2); ZDO val3 = Record(); val3.Deserialize(new ZPackage(val2.GetArray())); Check(BoardSchema.Kind(val3) == 2 && val3.GetString("whiteboard_pixels_v1", "") == text2 && val3.GetInt("whiteboard_revision_v1", 0) == 12, "actual game serializer preserves bitmap, marker and revision"); val3.Set(ZDOVars.s_text, "Vanilla label edit"); ZPackage val4 = new ZPackage(); val3.Serialize(val4); ZDO val5 = Record(); val5.Deserialize(new ZPackage(val4.GetArray())); Check(val5.GetString(ZDOVars.s_text, "") == "Vanilla label edit" && val5.GetString("whiteboard_pixels_v1", "") == text2 && BoardSchema.Kind(val5) == 2, "actual vanilla text update roundtrip retains opaque chalkboard fields"); Check(scratch.All((ZDO z) => ZDOMan.instance.GetZDO(z.m_uid) == null), "QA records never entered world state"); if (GUIManager.IsHeadless() && Array.IndexOf(Environment.GetCommandLineArgs(), "-chalkboard-validate") >= 0) { RuntimeWireTests.Run(Check); RuntimeWorldTests.Placement(Check); if (Array.IndexOf(Environment.GetCommandLineArgs(), "-chalkboard-world-seed") >= 0) { RuntimeWorldTests.Seed(Check); } if (Array.IndexOf(Environment.GetCommandLineArgs(), "-chalkboard-world-verify") >= 0) { RuntimeWorldTests.Verify(Check); } } messages.Add("PASS runtime smoke suite; " + messages.Count + " checks. Live mixed-client transport is a separate gate."); } catch (Exception ex) { messages.Add("FAIL " + ex); } finally { foreach (ZDO item in scratch) { item.Reset(); } } string text3 = string.Join(Environment.NewLine, messages); string text4 = Path.Combine(Paths.ConfigPath, "ValheimChalkboard", "validation"); Directory.CreateDirectory(text4); string text5 = Path.Combine(text4, "runtime-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff") + ".txt"); File.WriteAllText(text5, text3); WhiteboardPlugin.LogInfo(text3 + Environment.NewLine + "Runtime report: " + text5); return text3; void Check(bool flag, string name) { if (!flag) { throw new InvalidOperationException(name); } messages.Add("PASS " + name); } ZDO Record() { //IL_003f: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown uint suffix; for (suffix = (uint)(-268435456 + scratch.Count); ZDOMan.instance.GetZDO(new ZDOID(ZNet.GetUID(), suffix)) != null || scratch.Any((ZDO z) => ((ZDOID)(ref z.m_uid)).Equals(new ZDOID(ZNet.GetUID(), suffix))); suffix++) { } ZDO val6 = new ZDO { m_uid = new ZDOID(ZNet.GetUID(), suffix) }; AccessTools.Method(typeof(ZDO), "Init", (Type[])null, (Type[])null).Invoke(val6, null); val6.SetOwner(ZNet.GetUID()); scratch.Add(val6); return val6; } } } internal static class RuntimeWireTests { public static void Run(Action<bool, string> check) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_0665: Unknown result type (might be due to invalid IL or missing references) //IL_070d: Unknown result type (might be due to invalid IL or missing references) //IL_0714: Expected O, but got Unknown ZRoutedRpc instance = ZRoutedRpc.instance; FieldInfo fieldInfo = AccessTools.Field(typeof(ZRoutedRpc), "s_instance"); List<ZNetPeer> list = (List<ZNetPeer>)AccessTools.Field(typeof(ZNet), "m_peers").GetValue(ZNet.instance); List<ZNetPeer> cleanup = new List<ZNetPeer>(); ZDO val = null; ZDO board = null; ZRoutedRpc val2 = new ZRoutedRpc(true); val2.SetUID(ZNet.GetUID()); ZRoutedRpc val3 = new ZRoutedRpc(false); val3.SetUID(9223372036854775806L); ZRoutedRpc val4 = new ZRoutedRpc(false); val4.SetUID(9223372036854775805L); fieldInfo.SetValue(null, val2); try { ZNetPeer[] array = Pair(9223372036854775806L); ZNetPeer[] array2 = Pair(9223372036854775805L); list.Add(array[0]); list.Add(array2[0]); val2.AddPeer(array[0]); val3.AddPeer(array[1]); val2.AddPeer(array2[0]); val4.AddPeer(array2[1]); BoardNetwork.Register(); bool arg = (bool)AccessTools.Method(typeof(Main).Assembly.GetType("Jotunn.Utils.ModCompatibility", throwOnError: true), "ZNet_RPC_PeerInfo", (Type[])null, (Type[])null).Invoke(null, new object[3] { ZNet.instance, array2[0].m_rpc, (object)new ZPackage() }); check(arg, "actual Jotunn peer-info gate admits an endpoint with no Jotunn version handshake"); bool hello = false; bool rejected = false; bool vanillaData = false; int stockRoutedMessages = 0; array2[1].m_rpc.Register<ZPackage>("RoutedRPC", (Action<ZRpc, ZPackage>)delegate { stockRoutedMessages++; }); val3.Register<int>("ChalkboardV2_HelloResult", (Action<long, int>)delegate(long sender, int protocol) { hello = sender == ZNet.GetUID() && protocol == 3; }); ZDOID missing = new ZDOID(ZNet.GetUID(), uint.MaxValue); Action<long, ZDOID, int, bool, int, string> onResult = delegate(long sender, ZDOID id, int num2, bool accepted, int revision, string pixels) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) rejected = sender == ZNet.GetUID() && ((ZDOID)(ref id)).Equals(missing) && num2 == 77 && !accepted && revision == 0 && pixels == BoardSchema.Blank; }; val3.Register<ZDOID, int, bool, int, string>("ChalkboardV2_Result", (Method<ZDOID, int, bool, int, string>)delegate(long sender, ZDOID id, int arg2, bool accepted, int revision, string pixels) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) onResult(sender, id, arg2, accepted, revision, pixels); }); val3.InvokeRoutedRPC(ZNet.GetUID(), "ChalkboardV2_Hello", new object[1] { 3 }); Pump(() => hello); check(hello, "real TCP/ZRpc/ZRoutedRpc capability handshake reaches production handler"); val3.InvokeRoutedRPC(ZNet.GetUID(), "ChalkboardV2_Save", new object[4] { missing, 77, 0, BoardSchema.Blank }); Pump(() => rejected); check(rejected, "production missing-board rejection survives actual five-argument wire acknowledgement"); Vector3 val5 = default(Vector3); ((Vector3)(ref val5))..ctor(3000f, 100f, 3000f); val = ZDOMan.instance.CreateNewZDO(val5, StringExtensionMethods.GetStableHashCode("Player")); val.SetPrefab(StringExtensionMethods.GetStableHashCode("Player")); val.Set(ZDOVars.s_playerID, 1234567L); val.Set(ZDOVars.s_dead, false); val.SetOwner(array[0].m_uid); array[0].m_characterID = val.m_uid; board = ZDOMan.instance.CreateNewZDO(val5, BoardSchema.SignHash); board.SetPrefab(BoardSchema.SignHash); BoardSchema.Initialize(board, 1); board.SetOwner(array2[0].m_uid); BoardNetwork.Track(board); check(BoardAccess.CanWrite(array[0].m_uid, val5), "actual peer character ZDO authorizes nearby writer without instantiated Player"); bool saved = false; byte[] array3 = new byte[4608]; BoardData.SetPixel(array3, 20, 30, 6); string encoded = Convert.ToBase64String(array3); onResult = delegate(long sender, ZDOID id, int num2, bool accepted, int revision, string pixels) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) saved = sender == ZNet.GetUID() && ((ZDOID)(ref id)).Equals(board.m_uid) && num2 == 78 && accepted && revision == 1 && pixels == encoded; }; val3.InvokeRoutedRPC(ZNet.GetUID(), "ChalkboardV2_Save", new object[4] { board.m_uid, 78, 0, encoded }); Pump(() => saved); check(saved && board.GetString("whiteboard_pixels_v1", "") == encoded && board.GetInt("whiteboard_revision_v1", 0) == 1 && board.GetOwner() == ZNet.GetUID(), "actual host accepts socket drawing write on stock-owned sign and returns canonical snapshot"); for (int num = 0; num < 5; num++) { int request = 79 + num; int expected = 2 + num; bool streamed = false; BoardData.SetPixel(array3, 21 + num, 30, 6); string frame = Convert.ToBase64String(array3); onResult = delegate(long sender, ZDOID id, int reply, bool accepted, int revision, string pixels) { streamed = reply == request && accepted && revision == expected && pixels == frame; }; val3.InvokeRoutedRPC(ZNet.GetUID(), "ChalkboardV2_Save", new object[4] { board.m_uid, request, 0, frame }); Pump(() => streamed); check(streamed && board.GetString("whiteboard_pixels_v1", "") == frame, "socket stream update " + num + " accepts last received snapshot with old base revision"); } bool pixelSaved = false; val3.Register<ZDOID, int, bool, int, string>("ChalkboardV3_PixelResult", (Method<ZDOID, int, bool, int, string>)delegate(long sender, ZDOID id, int num2, bool accepted, int revision, string delta) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) pixelSaved = ((ZDOID)(ref id)).Equals(board.m_uid) && num2 == 100 && accepted && delta == "42:3"; }); val3.InvokeRoutedRPC(ZNet.GetUID(), "ChalkboardV3_Pixel", new object[4] { board.m_uid, 100, 42, 3 }); Pump(() => pixelSaved); check(pixelSaved && BoardData.Pixel(Convert.FromBase64String(board.GetString("whiteboard_pixels_v1", "")), 42, 0) == 3, "individual pixel update and compact acknowledgement traverse actual sockets"); array2[1].m_rpc.Register<ZPackage>("ChalkboardQA_StockData", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { vanillaData = package.ReadInt() == BoardSchema.SignHash && package.ReadString() == "Chalkboard"; }); ZPackage val6 = new ZPackage(); val6.Write(BoardSchema.SignHash); val6.Write("Chalkboard"); array2[0].m_rpc.Invoke("ChalkboardQA_StockData", new object[1] { val6 }); Pump(() => vanillaData); check(vanillaData, "stock endpoint without chalkboard handlers receives ordinary sign data over real socket"); check(stockRoutedMessages == 0, "targeted handshake/save replies do not arrive at the stock socket"); } finally { if (val != null) { val.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(val); } if (board != null) { board.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(board); } foreach (ZNetPeer item in cleanup) { list.Remove(item); item.m_rpc.Dispose(); } fieldInfo.SetValue(null, instance); AccessTools.Field(typeof(BoardNetwork), "router").SetValue(null, instance); } ZNetPeer[] Pair(long clientId) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_005b: 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: Expected O, but got Unknown TcpListener tcpListener = new TcpListener(IPAddress.Loopback, 0); tcpListener.Start(); try { TcpClient tcpClient = new TcpClient(); tcpClient.Connect(IPAddress.Loopback, ((IPEndPoint)tcpListener.LocalEndpoint).Port); ZNetPeer val7 = new ZNetPeer((ISocket)new ZSocket2(tcpListener.AcceptTcpClient(), "localhost"), false) { m_uid = clientId }; ZNetPeer val8 = new ZNetPeer((ISocket)new ZSocket2(tcpClient, "localhost"), true) { m_uid = ZNet.GetUID() }; cleanup.Add(val7); cleanup.Add(val8); return (ZNetPeer[])(object)new ZNetPeer[2] { val7, val8 }; } finally { tcpListener.Stop(); } } void Pump(Func<bool> done) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); while (!done() && stopwatch.ElapsedMilliseconds < 5000) { foreach (ZNetPeer item2 in cleanup) { item2.m_rpc.Update(0.01f); } Thread.Sleep(2); } } } } internal static class RuntimeWorldTests { private const string Tag = "chalkboard_disposable_qa_fixture"; private static List<ZDO> Fixtures() { return ((Dictionary<ZDOID, ZDO>)AccessTools.Field(typeof(ZDOMan), "m_objectsByID").GetValue(ZDOMan.instance)).Values.Where((ZDO z) => z.GetInt("chalkboard_disposable_qa_fixture", 0) != 0).ToList(); } public static void Placement(Action<bool, string> check) { //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) GameObject prefab = PrefabManager.Instance.GetPrefab("sign"); for (int i = 1; i <= 3; i++) { GameObject val = null; try { val = Object.Instantiate<GameObject>(PrefabManager.Instance.GetPrefab(BoardSchema.LegacyNames[i - 1]), new Vector3((float)(4000 + i * 10), 100f, 4000f), Quaternion.identity); ZDO zDO = val.GetComponent<ZNetView>().GetZDO(); check(zDO != null && zDO.GetPrefab() == BoardSchema.SignHash && BoardSchema.Kind(zDO) == i, "actual Unity placement creates marked vanilla sign from the first Awake: " + i); SignDecoration component = val.GetComponent<SignDecoration>(); AccessTools.Method(typeof(SignDecoration), "Update", (Type[])null, (Type[])null).Invoke(component, null); check((Object)(object)val.GetComponent<Sign>() != (Object)null && (Object)(object)val.GetComponent<DrawingBoard>() != (Object)null && (Object)(object)val.transform.Find("ChalkboardDecoration") != (Object)null, "actual marked sign builds its local drawing decoration: " + i); check(val.GetComponentsInChildren<Collider>(true).Length == prefab.GetComponentsInChildren<Collider>(true).Length, "constructed decoration adds no invisible solid colliders: " + i); } finally { if ((Object)(object)val != (Object)null) { ZNetScene.instance.Destroy(val); } } } } public static void Seed(Action<bool, string> check) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) check(Fixtures().Count == 0, "disposable world has no previous migration fixtures"); for (int i = 1; i <= 3; i++) { int stableHashCode = StringExtensionMethods.GetStableHashCode(BoardSchema.LegacyNames[i - 1]); ZDO obj = ZDOMan.instance.CreateNewZDO(new Vector3((float)(5000 + i * 100), 100f, 5000f), stableHashCode); obj.SetPrefab(stableHashCode); obj.Persistent = true; obj.Set("chalkboard_disposable_qa_fixture", i); obj.Set("health", 73f); obj.Set("creator", 123456L); byte[] array = new byte[4608]; BoardData.SetPixel(array, i, i, (byte)(i + 1)); obj.Set("whiteboard_pixels_v1", Convert.ToBase64String(array)); obj.Set("whiteboard_revision_v1", i * 10); } MigrationPersistence.Save(); check(Fixtures().Count == 3 && Fixtures().All((ZDO z) => BoardSchema.LegacyKind(z.GetPrefab()) != 0), "three unloaded legacy boards committed with the actual world save pipeline"); } public static void Verify(Action<bool, string> check) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = Fixtures(); check(list.Count == 3, "actual restarted world contains all three unloaded migration fixtures"); foreach (ZDO item in list) { int num = item.GetInt("chalkboard_disposable_qa_fixture", 0); check(BoardSchema.Kind(item) == num && item.GetPrefab() == BoardSchema.SignHash, "restarted world migrated fixture to vanilla sign: " + num); check(item.GetInt("whiteboard_revision_v1", 0) == num * 10 && BoardData.TryDecode(item.GetString("whiteboard_pixels_v1", ""), out var pixels) && BoardData.Pixel(pixels, num, num) == num + 1 && item.GetFloat("health", 0f) == 73f && item.GetLong("creator", 0L) == 123456 && item.GetPosition().x == (float)(5000 + num * 100), "actual disk load/migration retains drawing, revision, health, creator and position: " + num); } check(BoardMigration.Persisted, "migrated world committed before network entry"); } } public sealed class SignDecoration : MonoBehaviour { private int decoratedKind; private float nextPoll; private GameObject visual; private DrawingBoard drawing; private readonly List<MeshRenderer> original = new List<MeshRenderer>(); private readonly List<Material> materials = new List<Material>(); private GameObject textObject; private bool textWasActive; private void Update() { if (Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 0.25f; ZNetView component = ((Component)this).GetComponent<ZNetView>(); BoardTemplate component2 = ((Component)this).GetComponent<BoardTemplate>(); int num = (((Object)(object)component != (Object)null && component.IsValid()) ? BoardSchema.Kind(component.GetZDO()) : (((Object)(object)component2 != (Object)null) ? component2.Kind : 0)); if (num != decoratedKind) { Clear(); if (BoardSchema.ValidKind(num)) { Decorate(num); } } } private void Decorate(int kind) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) TMP_Text componentInChildren = ((Component)this).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren == (Object)null) { return; } Material val = null; MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val2 in componentsInChildren) { if (!((Object)(object)((Component)val2).GetComponent<TMP_Text>() != (Object)null)) { if ((Object)(object)val == (Object)null) { val = ((Renderer)val2).sharedMaterial; } if (((Renderer)val2).enabled) { original.Add(val2); ((Renderer)val2).enabled = false; } } } if ((Object)(object)val == (Object)null) { Clear(); return; } textObject = ((Component)componentInChildren).gameObject; textWasActive = textObject.activeSelf; textObj