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 BetterBuilding v0.14.1
BetterBuilding.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Utilities; using UnityEngine.Rendering; 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("BetterBuilding")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.14.1.0")] [assembly: AssemblyInformationalVersion("0.14.1")] [assembly: AssemblyProduct("BetterBuilding")] [assembly: AssemblyTitle("BetterBuilding")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.14.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BetterBuilding { internal static class BlueprintBinaryCodec { private sealed class BoundedBuffer : MemoryStream { private readonly int _maximum; internal BoundedBuffer(int maximum) { _maximum = maximum; } private void Check(long length) { if (length > _maximum) { throw new ArgumentException("Blueprint data exceeds the supported storage size.", "maximumBytes"); } } public override void Write(byte[] buffer, int offset, int count) { Check(Position + count); base.Write(buffer, offset, count); } public override void Write(ReadOnlySpan<byte> buffer) { Check(Position + buffer.Length); base.Write(buffer); } public override void WriteByte(byte value) { Check(Position + 1); base.WriteByte(value); } public override void SetLength(long value) { Check(value); base.SetLength(value); } } internal const int MaximumRawBytes = 67108864; private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); internal static byte[] Compress(BlueprintData data, int maximumPackedBytes, out int rawLength) { data.Validate(); using BoundedBuffer boundedBuffer = new BoundedBuffer(67108864); using (BinaryWriter binaryWriter = new BinaryWriter(boundedBuffer, Utf8, leaveOpen: true)) { binaryWriter.Write(data.formatVersion); binaryWriter.Write(data.name); string[] array = data.pieces.Select((BlueprintPiece p) => p.prefab).Distinct().ToArray(); Dictionary<string, int> dictionary = array.Select((string name, int id) => (name: name, id: id)).ToDictionary(((string name, int id) p) => p.name, ((string name, int id) p) => p.id); binaryWriter.Write(array.Length); string[] array2 = array; foreach (string value in array2) { binaryWriter.Write(value); } binaryWriter.Write(data.pieces.Length); BlueprintPiece[] pieces = data.pieces; foreach (BlueprintPiece blueprintPiece in pieces) { binaryWriter.Write(dictionary[blueprintPiece.prefab]); binaryWriter.Write(blueprintPiece.x); binaryWriter.Write(blueprintPiece.y); binaryWriter.Write(blueprintPiece.z); binaryWriter.Write(blueprintPiece.qx); binaryWriter.Write(blueprintPiece.qy); binaryWriter.Write(blueprintPiece.qz); binaryWriter.Write(blueprintPiece.qw); } binaryWriter.Write(data.terrain != null); if (data.terrain != null) { BlueprintTerrain terrain = data.terrain; binaryWriter.Write(terrain.version); binaryWriter.Write(terrain.width); binaryWriter.Write(terrain.spacing); binaryWriter.Write(terrain.radius); binaryWriter.Write(terrain.originX); binaryWriter.Write(terrain.originZ); float[] heights = terrain.heights; foreach (float value2 in heights) { binaryWriter.Write(value2); } } if (data.formatVersion >= 2) { binaryWriter.Write(data.pieces.Count((BlueprintPiece p) => p.attachment != null)); for (int num2 = 0; num2 < data.pieces.Length; num2++) { BlueprintAttachment attachment = data.pieces[num2].attachment; if (attachment != null) { binaryWriter.Write(num2); binaryWriter.Write(attachment.prefab); binaryWriter.Write(attachment.variant); binaryWriter.Write(attachment.orientation); } } } if (data.formatVersion >= 3) { binaryWriter.Write(data.pieces.Count((BlueprintPiece p) => p.signText != null)); for (int num3 = 0; num3 < data.pieces.Length; num3++) { if (data.pieces[num3].signText != null) { binaryWriter.Write(num3); binaryWriter.Write(data.pieces[num3].signText); } } } if (data.formatVersion >= 4) { binaryWriter.Write(data.pieces.Count((BlueprintPiece p) => p.scale != null)); for (int num4 = 0; num4 < data.pieces.Length; num4++) { BlueprintScale scale = data.pieces[num4].scale; if (scale != null) { binaryWriter.Write(num4); binaryWriter.Write(scale.x); binaryWriter.Write(scale.y); binaryWriter.Write(scale.z); } } } } rawLength = checked((int)boundedBuffer.Length); using BoundedBuffer boundedBuffer2 = new BoundedBuffer(maximumPackedBytes); using (DeflateStream deflateStream = new DeflateStream(boundedBuffer2, CompressionLevel.Optimal, leaveOpen: true)) { deflateStream.Write(boundedBuffer.GetBuffer(), 0, rawLength); } return boundedBuffer2.ToArray(); } internal static BlueprintData Expand(byte[] packed, int maximumPieces, int expectedRawLength = -1) { if (maximumPieces < 1 || maximumPieces > 250000) { throw new ArgumentException("Invalid blueprint piece limit."); } if (packed == null || packed.Length == 0 || packed.Length > 67108864 || expectedRawLength == 0 || expectedRawLength < -1 || expectedRawLength > 67108864) { throw new ArgumentException("Invalid compressed blueprint size."); } using MemoryStream stream = new MemoryStream(packed, writable: false); using DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress); using BoundedBuffer boundedBuffer = new BoundedBuffer((expectedRawLength < 0) ? 67108864 : expectedRawLength); byte[] array = new byte[8192]; int count; while ((count = deflateStream.Read(array, 0, array.Length)) > 0) { boundedBuffer.Write(array, 0, count); } if (expectedRawLength >= 0 && boundedBuffer.Length != expectedRawLength) { throw new InvalidDataException("Incomplete blueprint data."); } boundedBuffer.Position = 0L; using BinaryReader binaryReader = new BinaryReader(boundedBuffer, Utf8, leaveOpen: true); int num = binaryReader.ReadInt32(); if (num != 1 && num != 2 && num != 3 && num != 4) { throw new ArgumentException("Unsupported blueprint payload version."); } BlueprintData blueprintData = new BlueprintData { formatVersion = num, name = ReadString(binaryReader, 80) }; int num2 = binaryReader.ReadInt32(); if (num2 < 1 || num2 > maximumPieces || num2 > boundedBuffer.Length - boundedBuffer.Position) { throw new ArgumentException("Invalid blueprint prefab count."); } string[] array2 = new string[num2]; for (int i = 0; i < num2; i++) { array2[i] = ReadString(binaryReader, 160); } int num3 = binaryReader.ReadInt32(); if (num3 < 1 || num3 > maximumPieces || num2 > num3) { throw new ArgumentException("The blueprint exceeds the active piece limit or is empty."); } RequireBytes(binaryReader, (long)num3 * 32L + 1); blueprintData.pieces = new BlueprintPiece[num3]; for (int j = 0; j < num3; j++) { int num4 = binaryReader.ReadInt32(); if (num4 < 0 || num4 >= num2) { throw new ArgumentException("Invalid blueprint prefab index."); } blueprintData.pieces[j] = new BlueprintPiece { prefab = array2[num4], x = binaryReader.ReadSingle(), y = binaryReader.ReadSingle(), z = binaryReader.ReadSingle(), qx = binaryReader.ReadSingle(), qy = binaryReader.ReadSingle(), qz = binaryReader.ReadSingle(), qw = binaryReader.ReadSingle() }; } byte num5 = binaryReader.ReadByte(); if (num5 > 1) { throw new ArgumentException("Invalid terrain flag."); } if (num5 == 1) { BlueprintTerrain obj = new BlueprintTerrain { version = binaryReader.ReadInt32(), width = binaryReader.ReadInt32(), spacing = binaryReader.ReadSingle(), radius = binaryReader.ReadSingle(), originX = binaryReader.ReadSingle(), originZ = binaryReader.ReadSingle() }; BlueprintTerrain blueprintTerrain = obj; blueprintData.terrain = obj; BlueprintTerrain blueprintTerrain2 = blueprintTerrain; if (blueprintTerrain2.width < 2 || blueprintTerrain2.width > 449) { throw new ArgumentException("Invalid blueprint terrain size."); } RequireBytes(binaryReader, (long)blueprintTerrain2.width * (long)blueprintTerrain2.width * 4); blueprintTerrain2.heights = new float[blueprintTerrain2.width * blueprintTerrain2.width]; for (int k = 0; k < blueprintTerrain2.heights.Length; k++) { blueprintTerrain2.heights[k] = binaryReader.ReadSingle(); } } if (num >= 2) { int num6 = binaryReader.ReadInt32(); if (num6 < 0 || num6 > num3) { throw new ArgumentException("Invalid display item count."); } RequireBytes(binaryReader, (long)num6 * 14L); for (int l = 0; l < num6; l++) { int num7 = binaryReader.ReadInt32(); if (num7 < 0 || num7 >= num3 || blueprintData.pieces[num7].attachment != null) { throw new ArgumentException("Invalid display item index."); } blueprintData.pieces[num7].attachment = new BlueprintAttachment { prefab = ReadString(binaryReader, 160), variant = binaryReader.ReadInt32(), orientation = binaryReader.ReadInt32() }; } } if (num >= 3) { int num8 = binaryReader.ReadInt32(); if (num8 < 0 || num8 > num3) { throw new ArgumentException("Invalid sign text count."); } RequireBytes(binaryReader, (long)num8 * 5L); for (int m = 0; m < num8; m++) { int num9 = binaryReader.ReadInt32(); if (num9 < 0 || num9 >= num3 || blueprintData.pieces[num9].signText != null) { throw new ArgumentException("Invalid sign text index."); } blueprintData.pieces[num9].signText = ReadString(binaryReader, 4096); } } if (num >= 4) { int num10 = binaryReader.ReadInt32(); if (num10 < 0 || num10 > num3) { throw new ArgumentException("Invalid scale count."); } RequireBytes(binaryReader, (long)num10 * 16L); for (int n = 0; n < num10; n++) { int num11 = binaryReader.ReadInt32(); if (num11 < 0 || num11 >= num3 || blueprintData.pieces[num11].scale != null) { throw new ArgumentException("Invalid scale index."); } blueprintData.pieces[num11].scale = new BlueprintScale { x = binaryReader.ReadSingle(), y = binaryReader.ReadSingle(), z = binaryReader.ReadSingle() }; } } if (boundedBuffer.Position != boundedBuffer.Length) { throw new ArgumentException("Unexpected trailing blueprint data."); } blueprintData.Validate(maximumPieces); return blueprintData; } private static string ReadString(BinaryReader reader, int maximumCharacters) { int num = 0; for (int i = 0; i < 21; i += 7) { byte b = reader.ReadByte(); num |= (b & 0x7F) << i; if (num > maximumCharacters * 3) { throw new ArgumentException("Blueprint text is too long."); } if ((b & 0x80) == 0) { RequireBytes(reader, num); string text = Utf8.GetString(reader.ReadBytes(num)); if (text.Length > maximumCharacters) { throw new ArgumentException("Blueprint text is too long."); } return text; } } throw new ArgumentException("Invalid blueprint text length."); } private static void RequireBytes(BinaryReader reader, long count) { if (count < 0 || count > reader.BaseStream.Length - reader.BaseStream.Position) { throw new InvalidDataException("Incomplete blueprint data."); } } } internal sealed class BlueprintBuilder : IDisposable { internal static BlueprintBuilder Active; private readonly Player _player; private readonly ItemData _tool; private readonly BlueprintData _data; private readonly Vector3 _origin; private readonly Quaternion _rotation; private readonly ServerSettings _policy; private readonly BlueprintProject _project; private readonly BlueprintProbe _probe; private readonly BlueprintConstructionGrace _wearGrace = new BlueprintConstructionGrace(); private int _index; private int _checkIndex; private bool _checked; private string _problem = ""; private float _waitingSince = -1f; private float _nextAreaCheck; private float _nextWaitMessage; internal bool CancelRequested; private readonly string _batchId = Guid.NewGuid().ToString("N"); internal string Status { get { if (!(_waitingSince >= 0f)) { return (_checked ? "Building" : "Checking placement") + " (" + (_checked ? _index : _checkIndex) + "/" + _data.pieces.Length + ")"; } return "Waiting for the area to load; construction continues automatically. (" + _index + "/" + _data.pieces.Length + ")"; } } internal bool HasStamp(ZDO zdo) { return zdo.GetString("vdb_batch", "") == _batchId; } internal void Protect(WearNTear piece) { _wearGrace.Track(piece, Time.time); } internal void Stamp(ZDO zdo) { zdo.Set("vdb_batch", _batchId); zdo.Set("vdb_batch_count", _data.pieces.Length); zdo.Set("vdb_batch_index", _index); if (Object.op_Implicit((Object)(object)_project) && _project.Recoverable) { _project.StampRecovery(zdo); } } internal static void Begin(Player player, BlueprintData data, Vector3 origin, Quaternion rotation, ServerSettings policy, BlueprintProject project) { //IL_0021: 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) if (Active != null) { throw new InvalidOperationException("Another blueprint is being built."); } data.Validate(policy.MaximumBlueprint); Active = new BlueprintBuilder(player, data, origin, rotation, policy, project); } private BlueprintBuilder(Player player, BlueprintData data, Vector3 origin, Quaternion rotation, ServerSettings policy, BlueprintProject project) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) _player = player; _tool = ((Humanoid)player).GetRightItem(); _data = data; _origin = origin; _rotation = rotation; _policy = policy; _project = project; _index = (Object.op_Implicit((Object)(object)project) ? project.Progress : 0); _checkIndex = _index; _probe = new BlueprintProbe(player, data, origin); } internal void Tick() { try { if (CancelRequested || RemovalSession.CancelPressed() || !Object.op_Implicit((Object)(object)_player) || (Object)(object)_player != (Object)(object)Player.m_localPlayer || ((Character)_player).IsDead() || ((Character)_player).IsTeleporting() || !Application.isFocused || ((Humanoid)_player).GetRightItem() != _tool || !((Character)_player).InPlaceMode() || !Plugin.Instance.PolicyMatches(_policy) || !_policy.Blueprints || (!Object.op_Implicit((Object)(object)_project) && (!((Character)_player).TakeInput() || Hud.IsPieceSelectionVisible())) || (Object.op_Implicit((Object)(object)_project) && (!_project.Locked || !_project.CanManage(_player) || !_policy.Projects))) { Interrupt("Building was cancelled, access/settings changed, or the player is no longer ready to build."); return; } if (Object.op_Implicit((Object)(object)_project) && _project.Recoverable && !_project.AttemptReady) { if (!_project.RecoveryBusy) { Stop(string.IsNullOrEmpty(_project.Problem) ? "Reservation not confirmed. Materials remain at the project chest." : _project.Problem); } return; } _wearGrace.Refresh(Time.time); if (_waitingSince >= 0f && Time.unscaledTime < _nextAreaCheck) { return; } Stopwatch stopwatch = Stopwatch.StartNew(); if (!_checked) { do { if (_checkIndex == _data.pieces.Length) { _checked = true; break; } BlueprintPiece blueprintPiece = _data.pieces[_checkIndex]; if (!Evaluate(blueprintPiece, place: false)) { PlacementFailed(blueprintPiece, _checkIndex); break; } _waitingSince = -1f; _checkIndex++; } while (stopwatch.ElapsedMilliseconds < 3); return; } do { if (_index >= _data.pieces.Length) { Complete(); break; } BlueprintPiece blueprintPiece2 = _data.pieces[_index]; GameObject prefab = BlueprintWorld.ResolveObject(blueprintPiece2.prefab); if (Object.op_Implicit((Object)(object)_project) ? (!_project.Funded()) : (!BlueprintWorld.Free(_player, prefab))) { Interrupt("Materials or free-building permission changed."); break; } if (!Evaluate(blueprintPiece2, place: true)) { PlacementFailed(blueprintPiece2, _index); break; } _waitingSince = -1f; if (Object.op_Implicit((Object)(object)_project)) { _project.Pay(_index, BlueprintWorld.Free(_player, prefab)); } _index++; if (_index == _data.pieces.Length) { Complete(); break; } } while (stopwatch.ElapsedMilliseconds < 3); } catch (Exception ex) { Plugin.Instance.LogNetwork("Blueprint construction stopped: " + ex); Interrupt("An unexpected error occurred; see the log for details."); } } private void PlacementFailed(BlueprintPiece piece, int index) { if (_probe.WaitingForArea) { float unscaledTime = Time.unscaledTime; if (_waitingSince < 0f) { _waitingSince = unscaledTime; _nextWaitMessage = unscaledTime + 1f; Plugin.Instance.LogNetwork("Blueprint waiting for area: " + _data.name + ", piece " + (index + 1) + "/" + _data.pieces.Length + " (" + piece.prefab + ")."); } _nextAreaCheck = unscaledTime + 0.25f; if (unscaledTime - _waitingSince < 60f) { if (unscaledTime >= _nextWaitMessage) { ((Character)_player).Message((MessageType)2, Status, 0, (Sprite)null, false); _nextWaitMessage = unscaledTime + 5f; } return; } _problem = "The destination area did not finish loading within 60 seconds. Stay near the construction area."; } Plugin.Instance.LogNetwork("Blueprint construction stopped: " + _data.name + ", piece " + (index + 1) + "/" + _data.pieces.Length + " (" + piece.prefab + "): " + _problem); Interrupt(_problem); } private void Interrupt(string reason) { string text = ((!Object.op_Implicit((Object)(object)_project)) ? "Pieces already placed remain." : (_project.Recoverable ? "Use Retry construction at the chest to clear this attempt, then Build again. Reserved materials are kept." : "Remaining materials stay at the chest. Build continues from the saved progress; already placed pieces are paid for.")); Stop("Construction stopped: " + reason + " " + text, Object.op_Implicit((Object)(object)_project) ? "Construction stopped. See the project chest for details." : ("Construction stopped: " + reason)); } private void Complete() { if (Object.op_Implicit((Object)(object)_project)) { _project.Finish(_player); Stop(_project.Recoverable ? "All parts placed. Waiting for project confirmation." : "Construction complete."); } else { Stop("Construction complete."); } } private bool Evaluate(BlueprintPiece p, bool place) { //IL_0071: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) Collider[] array = (Object.op_Implicit((Object)(object)_project) ? ((Component)_project).GetComponentsInChildren<Collider>() : Array.Empty<Collider>()); bool[] array2 = Array.ConvertAll(array, (Collider c) => c.enabled); try { Collider[] array3 = array; for (int num = 0; num < array3.Length; num++) { array3[num].enabled = false; } return _probe.EvaluateObject(BlueprintWorld.ResolveObject(p.prefab), _origin + _rotation * BlueprintWorld.Position(p), _rotation * BlueprintWorld.Rotation(p), _policy, place, out _problem, p.attachment, p.signText, p.scale); } finally { for (int num2 = 0; num2 < array.Length; num2++) { if (Object.op_Implicit((Object)(object)array[num2])) { array[num2].enabled = array2[num2]; } } } } private void Stop(string message, string notice = null) { if (Object.op_Implicit((Object)(object)_project)) { _project.Problem = message; } if (Object.op_Implicit((Object)(object)_player)) { ((Character)_player).Message((MessageType)2, (notice ?? message) + " (" + _index + "/" + _data.pieces.Length + ")", 0, (Sprite)null, false); } Dispose(); } public void Dispose() { _wearGrace.Close(Time.time); try { _probe.Dispose(); } finally { if (Active == this) { Active = null; } } } } internal sealed class BlueprintClipboard { private byte[] _snapshot; internal bool HasData => _snapshot != null; internal int Count { get; private set; } internal void Store(BlueprintData data) { byte[] snapshot = BlueprintFileCodec.Encode(data); _snapshot = snapshot; Count = data.pieces.Length; } internal BlueprintData Read(int maximum) { if (!HasData) { throw new InvalidOperationException("Copy a selection first."); } return BlueprintFileCodec.Decode(_snapshot, maximum); } } internal static class BlueprintShortcuts { private static bool _reading; private static readonly BuildingKey[] SelectionKeys = new BuildingKey[6] { BuildingControls.Area, BuildingControls.Copy, BuildingControls.Paste, BuildingControls.Subtract, BuildingControls.AreaHeight, BuildingControls.Cancel }; private static readonly BuildingKey[] PreviewKeys = new BuildingKey[5] { BuildingControls.Paste, BuildingControls.PausePreview, BuildingControls.PreviewHeight, BuildingControls.FineRotation, BuildingControls.Cancel }; internal static bool OwnsModifier { get { Plugin instance = Plugin.Instance; if (instance == null) { return false; } return instance.Blueprint?.AcceptsShortcuts == true; } } internal static bool Consumes(string name) { //IL_0219: 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) //IL_0222: Unknown result type (might be due to invalid IL or missing references) if (_reading || !Application.isFocused || BuildingControls.SystemChord) { return false; } _reading = true; try { Plugin instance = Plugin.Instance; if (SettingsMenu.Visible) { return true; } bool gameplayInput = BuildingCamera.GameplayInput; if (gameplayInput && (BuildingControls.Settings.Held || BuildingControls.Settings.Down) && BuildingControls.Settings.MatchesNative(name)) { return true; } if (instance != null && instance.Support?.CanToggle == true && (BuildingControls.Support.Held || BuildingControls.Support.Down) && BuildingControls.Support.MatchesNative(name)) { return true; } if ((instance?.FreeCamera.Active ?? false) && gameplayInput) { BuildingKey[] flight = BuildingControls.Flight; foreach (BuildingKey buildingKey in flight) { if ((buildingKey.Held || buildingKey.Down) && buildingKey.MatchesNative(name)) { return true; } } } if ((instance?.CanBuild ?? false) && gameplayInput && Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).InPlaceMode()) { BuildingKey[] flight = new BuildingKey[2] { BuildingControls.Blueprint, BuildingControls.Camera }; foreach (BuildingKey buildingKey2 in flight) { if ((buildingKey2.Held || buildingKey2.Down) && buildingKey2.MatchesNative(name)) { return true; } } } if (OwnsModifier) { BuildingKey[] flight = ((instance.Blueprint.Data == null) ? SelectionKeys : PreviewKeys); foreach (BuildingKey buildingKey3 in flight) { if ((buildingKey3.Held || buildingKey3.Down) && buildingKey3.MatchesNative(name)) { return true; } } flight = new BuildingKey[2] { BuildingControls.Select, BuildingControls.Menu }; foreach (BuildingKey buildingKey4 in flight) { KeyboardShortcut shortcut = buildingKey4.Shortcut; if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && (buildingKey4.Held || buildingKey4.Down) && buildingKey4.MatchesNative(name)) { return true; } } } return false; } finally { _reading = false; } } } [HarmonyPatch(typeof(ZInput), "GetButtonDown")] internal static class BlueprintShortcutButtons { private static void Postfix(string name, ref bool __result) { if (__result && BlueprintShortcuts.Consumes(name)) { __result = false; } } } [HarmonyPatch(typeof(ZInput), "GetButton")] internal static class BlueprintShortcutCrouch { private static void Postfix(string name, ref bool __result) { if (__result && BlueprintShortcuts.Consumes(name)) { __result = false; } } } [HarmonyPatch(typeof(Player), "TestGhostClipping")] internal static class BlueprintClippingPatch { internal static Collider[] Obstacles(Collider[] colliders, Player player) { BlueprintBuilder builder = BlueprintBuilder.Active; if (builder == null || !Object.op_Implicit((Object)(object)BlueprintProbe.ActivePrefab) || (Object)(object)PlacementEvaluation.Current?.Player != (Object)(object)player || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return colliders; } return Array.FindAll(colliders, (Collider c) => !Object.op_Implicit((Object)(object)c) || !BlueprintProbe.BuiltHere(c, builder)); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo overlap = AccessTools.Method(typeof(Physics), "OverlapSphere", new Type[3] { typeof(Vector3), typeof(float), typeof(int) }, (Type[])null); MethodInfo filter = AccessTools.Method(typeof(BlueprintClippingPatch), "Obstacles", (Type[])null, (Type[])null); int count = 0; foreach (CodeInstruction instruction in instructions) { yield return instruction; if (CodeInstructionExtensions.Calls(instruction, overlap)) { count++; yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)filter); } } if (count != 1) { throw new InvalidOperationException("Expected one native ghost overlap query, found " + count); } } } internal sealed class BlueprintConstructionGrace { internal const float SettleSeconds = 30f; private static ConditionalWeakTable<WearNTear, BlueprintConstructionGrace> Pieces = new ConditionalWeakTable<WearNTear, BlueprintConstructionGrace>(); private float _until; private bool _closed; internal void Refresh(float now) { if (!_closed) { _until = now + 30f; } } internal void Track(WearNTear piece, float now) { if (!_closed && Object.op_Implicit((Object)(object)piece)) { Refresh(now); Pieces.Remove(piece); Pieces.Add(piece, this); } } internal void Close(float now) { if (!_closed) { Refresh(now); _closed = true; } } internal static bool Holds(WearNTear piece, float now) { if (!Object.op_Implicit((Object)(object)piece) || !Pieces.TryGetValue(piece, out var value)) { return false; } if (now < value._until) { return true; } Pieces.Remove(piece); return false; } internal static void Clear() { Pieces = new ConditionalWeakTable<WearNTear, BlueprintConstructionGrace>(); } } [HarmonyPatch(typeof(WearNTear), "ShouldUpdate")] internal static class BlueprintConstructionWear { private static void Postfix(WearNTear __instance, float time, ref bool __result) { if (__result && (BlueprintConstructionGrace.Holds(__instance, time) || BlueprintRecovery.Pending((Component)(object)__instance))) { __result = false; } } } [Serializable] public sealed class BlueprintData { public int formatVersion = 1; public string name = "Copied building"; public BlueprintPiece[] pieces = Array.Empty<BlueprintPiece>(); public BlueprintTerrain terrain; public const int DefaultLimit = 100000; public const int HardLimit = 250000; public const int MaximumJsonBytes = 67108864; public void Validate(int maximum = 250000) { if (formatVersion != 1 && formatVersion != 2 && formatVersion != 3 && formatVersion != 4) { throw new ArgumentException("Unsupported blueprint format version."); } if (string.IsNullOrWhiteSpace(name) || name.Length > 80) { throw new ArgumentException("Blueprint names must contain 1–80 characters."); } if (pieces == null || pieces.Length == 0 || pieces.Length > Math.Min(maximum, 250000)) { throw new ArgumentException("The blueprint exceeds the active piece limit or is empty."); } BlueprintPiece[] array = pieces; foreach (BlueprintPiece blueprintPiece in array) { if (blueprintPiece == null || string.IsNullOrWhiteSpace(blueprintPiece.prefab) || blueprintPiece.prefab.Length > 160 || blueprintPiece.prefab.IndexOfAny(new char[3] { '/', '\\', '\0' }) >= 0) { throw new ArgumentException("Invalid blueprint prefab name."); } if (!Finite(blueprintPiece.x) || !Finite(blueprintPiece.y) || !Finite(blueprintPiece.z) || Math.Abs(blueprintPiece.x) > 150f || Math.Abs(blueprintPiece.y) > 150f || Math.Abs(blueprintPiece.z) > 150f) { throw new ArgumentException("Blueprint coordinates exceed the supported bounds."); } double num = (double)blueprintPiece.qx * (double)blueprintPiece.qx + (double)blueprintPiece.qy * (double)blueprintPiece.qy + (double)blueprintPiece.qz * (double)blueprintPiece.qz + (double)blueprintPiece.qw * (double)blueprintPiece.qw; if (!Finite(blueprintPiece.qx) || !Finite(blueprintPiece.qy) || !Finite(blueprintPiece.qz) || !Finite(blueprintPiece.qw) || Math.Abs(num - 1.0) > 0.005) { throw new ArgumentException("Invalid blueprint rotation."); } if (blueprintPiece.scale != null) { if (formatVersion < 4) { throw new ArgumentException("Natural-object scale requires blueprint format 4."); } blueprintPiece.scale.Validate(); } if (blueprintPiece.attachment != null) { if (formatVersion < 2) { throw new ArgumentException("Display items require blueprint format 2."); } blueprintPiece.attachment.Validate(); } if (blueprintPiece.signText != null && (formatVersion < 3 || blueprintPiece.signText.Length > 4096 || blueprintPiece.signText.IndexOf('\0') >= 0)) { throw new ArgumentException("Invalid sign text or blueprint format."); } } terrain?.Validate(); } internal static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } [Serializable] public sealed class BlueprintPiece { public const int MaximumSignCharacters = 4096; public string prefab; public string signText; public BlueprintAttachment attachment; public BlueprintScale scale; public float x; public float y; public float z; public float qx; public float qy; public float qz; public float qw = 1f; } [Serializable] public sealed class BlueprintScale { public float x = 1f; public float y = 1f; public float z = 1f; internal void Validate() { if (!BlueprintData.Finite(x) || !BlueprintData.Finite(y) || !BlueprintData.Finite(z) || x < 0.1f || y < 0.1f || z < 0.1f || x > 10f || y > 10f || z > 10f) { throw new ArgumentException("Invalid natural-object scale (supported range: 0.1 to 10)."); } } } [Serializable] public sealed class BlueprintAttachment { public string prefab; public int variant; public int orientation; internal void Validate() { if (string.IsNullOrWhiteSpace(prefab) || prefab.Length > 160 || prefab.IndexOfAny(new char[3] { '/', '\\', '\0' }) >= 0 || variant < 0 || variant > 255 || orientation < 0 || orientation > 63) { throw new ArgumentException("Invalid blueprint display item."); } } } [Serializable] public sealed class BlueprintTerrain { public int version = 1; public int width; public float spacing = 1f; public float radius; public float originX; public float originZ; public float[] heights; internal void Validate() { if ((version != 1 && version != 2) || width < 2 || width > ((version == 1) ? 129 : 449) || heights == null || heights.Length != width * width || !BlueprintData.Finite(spacing) || spacing < 0.25f || spacing > 4f || !BlueprintData.Finite(radius) || radius <= 0f || radius > (float)((version == 1) ? 64 : 224) || !BlueprintData.Finite(originX) || !BlueprintData.Finite(originZ) || Math.Abs(originX) > 450f || Math.Abs(originZ) > 450f) { throw new ArgumentException("Invalid optional terrain profile."); } if (version == 2 && (spacing != 1f || (float)width != radius * 2f + 1f)) { throw new ArgumentException("Invalid terrain profile geometry."); } float[] array = heights; foreach (float value in array) { if (!BlueprintData.Finite(value) || Math.Abs(value) > 150f) { throw new ArgumentException("Invalid terrain height."); } } } } internal static class BlueprintBudget { internal const int MaximumMaterialTotal = 100000000; internal static Dictionary<string, int> Sum(IEnumerable<KeyValuePair<string, int>> costs) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (KeyValuePair<string, int> cost in costs) { if (string.IsNullOrEmpty(cost.Key) || cost.Value < 0) { throw new ArgumentException("Invalid recipe."); } dictionary.TryGetValue(cost.Key, out var value); int num = checked(value + cost.Value); if (num > 100000000) { throw new ArgumentException("Material total is too large."); } if (num > 0) { dictionary[cost.Key] = num; } } return dictionary; } internal static int Deposit(int required, int delivered, int available) { return Math.Max(0, Math.Min(Math.Max(0, required - delivered), available)); } } internal static class BlueprintDecorations { internal static bool TrayPiece(GameObject prefab) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 ItemDrop component = prefab.GetComponent<ItemDrop>(); Piece component2 = prefab.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)prefab.GetComponent<WearNTear>())) { return (int)component.m_itemData.m_shared.m_itemType == 2; } return false; } internal static BlueprintAttachment Capture(Piece piece) { ItemStand component = ((Component)piece).GetComponent<ItemStand>(); if (!Object.op_Implicit((Object)(object)component) || !component.HaveAttachment()) { return null; } if (Object.op_Implicit((Object)(object)component.m_netViewOverride)) { throw new ArgumentException("Item stands with a separate network owner are not supported."); } ZDO zDO = ((Component)piece).GetComponent<ZNetView>().GetZDO(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(component.GetAttachedItem()); if (!Object.op_Implicit((Object)(object)itemPrefab)) { throw new ArgumentException("The displayed item is unavailable."); } BlueprintAttachment blueprintAttachment = new BlueprintAttachment { prefab = ((Object)itemPrefab).name, variant = zDO.GetInt(ZDOVars.s_variant, 0), orientation = zDO.GetInt(ZDOVars.s_type, 0) }; Resolve(piece, blueprintAttachment); return blueprintAttachment; } internal static ItemDrop Resolve(Piece piece, BlueprintAttachment attachment) { if (attachment == null) { return null; } attachment.Validate(); ItemStand component = ((Component)piece).GetComponent<ItemStand>(); object obj; if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { obj = null; } else { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(attachment.prefab); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } ItemDrop val = (ItemDrop)obj; if (!Object.op_Implicit((Object)(object)component) || Object.op_Implicit((Object)(object)component.m_netViewOverride) || !Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((Component)val).GetComponent<ZNetView>())) { throw new ArgumentException("Unavailable display item or item stand: " + attachment.prefab); } ItemData val2 = val.m_itemData.Clone(); val2.m_dropPrefab = ((Component)val).gameObject; if (!component.CanAttach(val2) || attachment.variant >= Math.Max(1, val2.m_shared.m_icons.Length)) { throw new ArgumentException("This item or style cannot be displayed on this stand: " + attachment.prefab); } List<OrientationSettings> list = new List<OrientationSettings>(); component.GetOrientationSettings(val2, ref list); if (attachment.orientation >= list.Count) { throw new ArgumentException("Invalid item stand orientation."); } return val; } internal static void Apply(ZNetView view, BlueprintAttachment attachment) { if (attachment != null) { ItemDrop val = Resolve(((Component)view).GetComponent<Piece>(), attachment); ItemData obj = val.m_itemData.Clone(); obj.m_dropPrefab = ((Component)val).gameObject; obj.m_stack = 1; obj.m_quality = 1; obj.m_variant = attachment.variant; obj.m_durability = obj.GetMaxDurability(); obj.m_customData = new Dictionary<string, string>(); obj.m_crafterID = 0L; obj.m_crafterName = ""; obj.m_worldLevel = (byte)Game.m_worldLevel; obj.m_cheated = Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.NoCostCheat(); ZDO zDO = view.GetZDO(); ItemDrop.SaveToZDO(obj, zDO, -1); zDO.Set(ZDOVars.s_quality, 1, false); zDO.Set(ZDOVars.s_variant, attachment.variant, false); zDO.Set(ZDOVars.s_item, StringExtensionMethods.GetStableHashCode(attachment.prefab), false); zDO.Set(ZDOVars.s_type, attachment.orientation, false); } } } [HarmonyPatch(typeof(StationExtension), "OtherExtensionInRange")] internal static class BlueprintExtensionSpacing { private static bool Prefix(StationExtension __instance, float radius, ref bool __result) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) PlacementEvaluation current = PlacementEvaluation.Current; BlueprintBuilder active = BlueprintBuilder.Active; if (active == null || !Object.op_Implicit((Object)(object)BlueprintProbe.ActivePrefab) || (Object)(object)current?.Player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || (Object)(object)Player.m_localPlayer.m_placementGhost != (Object)(object)((Component)__instance).gameObject) { return true; } foreach (StationExtension allExtension in StationExtension.m_allExtensions) { if (Object.op_Implicit((Object)(object)allExtension) && !((Object)(object)allExtension == (Object)(object)__instance) && !(Vector3.Distance(((Component)allExtension).transform.position, ((Component)__instance).transform.position) >= radius)) { ZNetView component = ((Component)allExtension).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !active.HasStamp(component.GetZDO())) { __result = true; return false; } } } __result = false; return false; } } internal static class BlueprintFileCodec { internal const int Magic = 1179664962; internal const int Version = 1; internal const int HeaderBytes = 48; internal const int MaximumFileBytes = 67108864; internal static byte[] Encode(BlueprintData data) { int rawLength; byte[] array = BlueprintBinaryCodec.Compress(data, 67108816, out rawLength); using SHA256 sHA = SHA256.Create(); using MemoryStream memoryStream = new MemoryStream(48 + array.Length); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true); binaryWriter.Write(1179664962); binaryWriter.Write(1); binaryWriter.Write(rawLength); binaryWriter.Write(array.Length); binaryWriter.Write(sHA.ComputeHash(array)); binaryWriter.Write(array); return memoryStream.ToArray(); } internal static BlueprintData Decode(byte[] bytes, int maximumPieces) { if (bytes == null || bytes.Length < 48 || bytes.Length > 67108864) { throw new ArgumentException("Invalid or oversized blueprint file."); } using MemoryStream input = new MemoryStream(bytes, writable: false); using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8, leaveOpen: true); if (binaryReader.ReadInt32() != 1179664962) { throw new ArgumentException("This is not a BetterBuilding blueprint file."); } if (binaryReader.ReadInt32() != 1) { throw new ArgumentException("Unsupported blueprint file version. Update BetterBuilding."); } int num = binaryReader.ReadInt32(); int num2 = binaryReader.ReadInt32(); if (num <= 0 || num > 67108864 || num2 <= 0 || num2 != bytes.Length - 48) { throw new InvalidDataException("The blueprint file is damaged or incomplete."); } byte[] array = binaryReader.ReadBytes(32); byte[] array2 = binaryReader.ReadBytes(num2); using SHA256 sHA = SHA256.Create(); if (!((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)sHA.ComputeHash(array2))) { throw new InvalidDataException("The blueprint file is damaged (checksum mismatch)."); } return BlueprintBinaryCodec.Expand(array2, maximumPieces, num); } } internal static class BlueprintMenu { private static readonly float[] FileColumns = new float[5] { 0.46f, 0.14f, 0.12f, 0.16f, 0.12f }; private static readonly float[] SearchColumns = new float[2] { 0.7f, 0.3f }; private static readonly float[] PageColumns = new float[3] { 0.25f, 0.5f, 0.25f }; private static readonly Dictionary<string, (string Size, string Modified)> _fileDetails = new Dictionary<string, (string, string)>(); private static string _fileSearch = ""; private static string _filteredSearch; private static string[] _filteredPaths = Array.Empty<string>(); private static int _filePage; private static bool _library; private static bool _libraryUploads; private static bool _librarySettings; private static bool _shareEnabled; private static bool _adminUploads; private static string _librarySearch = ""; private static string _downloadName = ""; private static string[] _uploadPaths = Array.Empty<string>(); private static string[] _libraryDraft; private static int _uploadPage; private static long _settingsRevision; private static double _libraryConnectAt; private static LibraryEntry _libraryDownload; private static LibraryEntry _libraryDelete; private static readonly float[] LibraryColumns = new float[6] { 0.42f, 0.11f, 0.12f, 0.11f, 0.12f, 0.12f }; private static readonly float[] LibrarySearchColumns = new float[3] { 0.56f, 0.22f, 0.22f }; private static readonly float[] AdminColumns = new float[2] { 0.5f, 0.5f }; internal static BlueprintProject Project; private static bool _files; private static bool _controls; private static bool _confirmCancel; private static PlanBuildImport _import; private static bool _saving; private static bool _overwrite; private static bool _terrain; private static bool _terrainChecked; private static string _fileName = "My building"; private static int _openedFrame = -1; private static string[] _paths = Array.Empty<string>(); private static string _error = ""; private static string _notice = ""; private static readonly BuildingPanel Panel = new BuildingPanel("Blueprint menu", modal: true); private static readonly Color _label = BuildPanelTheme.Body; private static readonly Color _hint = BuildPanelTheme.Hint; private static readonly Color _ready = BuildPanelTheme.Ready; private static readonly Color _warning = BuildPanelTheme.Warning; private static bool CanPositionBlueprint { get { if (Object.op_Implicit((Object)(object)Plugin.Instance) && Plugin.Instance.CanBlueprint && Object.op_Implicit((Object)(object)Player.m_localPlayer) && ((Character)Player.m_localPlayer).InPlaceMode() && BlueprintHammerEntry.IsHammer(((Humanoid)Player.m_localPlayer).GetRightItem())) { return BlueprintBuilder.Active == null; } return false; } } private static bool LibraryTableVisible { get { if (_library && !_librarySettings && _libraryDownload == null) { return _libraryDelete == null; } return false; } } internal static bool ManagementOpen { get; private set; } internal static bool Visible { get { if (!Object.op_Implicit((Object)(object)Project)) { return ControlsVisible; } return true; } } internal static bool EditingText { get { if (!_saving && !_library) { return _files; } return true; } } internal static bool ControlsVisible { get { if (!_files && !_controls && !_saving && !_terrain && _import == null) { return _library; } return true; } } private static string FileSize(long bytes) { if (bytes < 1048576) { return $"{(double)bytes / 1024.0:0.#} KiB"; } return $"{(double)bytes / 1048576.0:0.##} MiB"; } private static (string Size, string Modified) FileDetails(string path) { if (_fileDetails.TryGetValue(path, out (string, string) value)) { return value; } try { FileInfo fileInfo = new FileInfo(path); value = (fileInfo.Exists ? (FileSize(fileInfo.Length), fileInfo.LastWriteTime.ToString("yyyy-MM-dd")) : ("Unavailable", "—")); } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { value = ("Unavailable", "—"); } _fileDetails[path] = value; return value; } private static void FileHeader() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) Panel.BeginColumns(32f, FileColumns); string[] array = new string[5] { "BLUEPRINT", "FORMAT", "FILE SIZE", "MODIFIED", "ACTION" }; foreach (string text in array) { Panel.Text(text, BuildPanelTheme.Title, 16); } Panel.EndColumns(); } private static bool FileRow(string path, int stripe, string action, bool enabled) { //IL_0056: 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_008e: Unknown result type (might be due to invalid IL or missing references) (string, string) tuple = FileDetails(path); Panel.BeginColumns(46f, FileColumns, stripe); Panel.Text(Path.GetFileNameWithoutExtension(path)); Panel.Text(BlueprintFiles.IsPlanBuild(path) ? "PlanBuild" : Path.GetExtension(path).ToUpperInvariant(), _hint, 16); Panel.Text(tuple.Item1, _hint, 16); Panel.Text(tuple.Item2, _hint, 16); Panel.Enabled = enabled; bool result = Panel.ButtonKey(action, action + ":" + path); Panel.EndColumns(); return result; } private static void DrawLocalFiles(bool input, ref Action action) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) Panel.BeginToolbar(); Panel.Text("Search local blueprints", _hint, 16); Panel.BeginColumns(40f, SearchColumns); _fileSearch = Panel.Input("local-blueprint-search", _fileSearch, 80); if (Panel.Button("Refresh files")) { action = OpenFiles; } Panel.EndColumns(); if (_filteredSearch != _fileSearch) { _filteredSearch = _fileSearch; _filePage = 0; Panel.ResetScroll(); _filteredPaths = _paths.Where((string p) => Path.GetFileName(p).IndexOf(_fileSearch, StringComparison.OrdinalIgnoreCase) >= 0).ToArray(); } Panel.Text($"{_filteredPaths.Length:N0} blueprints · BBP, JSON and PlanBuild", _hint, 16); if (!CanPositionBlueprint) { Panel.Text("Equip a hammer to load and position a blueprint. Browsing and server sharing are available without one.", _hint, 16); } FileHeader(); Panel.EndToolbar(); int num = _filePage * 12; for (int num2 = num; num2 < Math.Min(num + 12, _filteredPaths.Length); num2++) { string path = _filteredPaths[num2]; if (FileRow(path, num2, "Load", input && CanPositionBlueprint)) { action = delegate { Load(path); }; } } Panel.Enabled = input; if (_filteredPaths.Length == 0) { Panel.Text((_paths.Length == 0) ? "No saved blueprints yet." : "No blueprints match your search.", _hint); } } private static int PageButtons(int offset, int total, bool input) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) int result = -1; Panel.BeginColumns(38f, PageColumns); Panel.Enabled = input && offset > 0; if (Panel.Button("Previous page")) { result = Math.Max(0, offset - 12); } Panel.Text($"Page {offset / 12 + 1} / {Math.Max(1, (total + 12 - 1) / 12)} · {total:N0} blueprints", _hint, 16); Panel.Enabled = input && offset + 12 < total; if (Panel.Button("Next page")) { result = offset + 12; } Panel.EndColumns(); Panel.Enabled = input; return result; } private static void DrawLocalFooter(bool input, ref Action action) { int next = PageButtons(_filePage * 12, _filteredPaths.Length, input); if (next >= 0) { action = delegate { _filePage = next / 12; Panel.ResetScroll(); }; } } private static void OpenLibrary() { _library = true; _controls = false; _libraryConnectAt = 0.0; Panel.ResetScroll(); if (Plugin.Instance.Library.Available) { Plugin.Instance.Library.Browse(0, _librarySearch); } } private static void CloseLibrary() { if (_library) { Plugin.Instance?.Library?.CancelClient(); } _library = (_libraryUploads = (_librarySettings = false)); _libraryDelete = (_libraryDownload = null); } private static void EditLibrarySettings() { LibraryPacket page = Plugin.Instance.Library.Page; LibraryConfiguration configuration = page.Configuration; _settingsRevision = page.Revision; _shareEnabled = configuration.Enabled; _adminUploads = configuration.AdminUploadsOnly; _libraryDraft = new int[6] { configuration.FileMiB, configuration.TotalMiB, configuration.MaximumFiles, configuration.FilesPerUser, configuration.MaximumPieces, configuration.CleanupSeconds }.Select((int n) => n.ToString()).ToArray(); _librarySettings = true; Panel.ResetScroll(); } private static void DrawLibrary(bool input, ref Action action) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0451: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Unknown result type (might be due to invalid IL or missing references) //IL_05aa: Unknown result type (might be due to invalid IL or missing references) //IL_0512: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_05de: Unknown result type (might be due to invalid IL or missing references) //IL_0417: 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_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_06be: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_07e9: Unknown result type (might be due to invalid IL or missing references) //IL_08a1: Unknown result type (might be due to invalid IL or missing references) //IL_08c9: Unknown result type (might be due to invalid IL or missing references) //IL_09f6: Unknown result type (might be due to invalid IL or missing references) //IL_08f9: Unknown result type (might be due to invalid IL or missing references) BlueprintLibrary library = Plugin.Instance.Library; if (!library.Available) { Panel.Text("This requires a compatible BetterBuilding installation on the server. Your local files remain available in the Local blueprint library.", _hint); return; } if (!LibraryTableVisible || library.Busy) { Panel.Text(library.Status, _label); if (library.Notice.Length > 0) { Panel.Text(library.Notice, _ready); } } if (library.Busy) { Panel.Text("Large blueprints are transferred in small packets. You can cancel safely before completion.", _hint); Panel.Enabled = input; if (Panel.Button("Cancel current request")) { action = delegate { library.CancelClient(); }; } return; } LibraryPacket page = library.Page; if (page == null) { if (Panel.Button("Connect / refresh") || Time.realtimeSinceStartupAsDouble >= _libraryConnectAt) { _libraryConnectAt = Time.realtimeSinceStartupAsDouble + 5.0; action = delegate { library.Browse(0, _librarySearch); }; } return; } if (_librarySettings) { if (!library.IsAdmin) { _librarySettings = false; return; } Panel.Text("Server settings · administrator", _ready); Panel.Text("Save applies immediately and persists on the server. Other clients cannot override these settings.", _hint); if (Panel.Button("Sharing: " + (_shareEnabled ? "ON" : "OFF"))) { _shareEnabled = !_shareEnabled; } if (Panel.Button("Uploads: " + (_adminUploads ? "admins only" : "all players"))) { _adminUploads = !_adminUploads; } string[] array = new string[6] { "Maximum file size (MiB, 1–64)", "Total storage (MiB, 1–16384)", "Maximum blueprints (1–10000)", "Uploads per account", "Archive piece limit (1–250000)", "Cleanup interval (seconds, 30–3600)" }; for (int num = 0; num < array.Length; num++) { Panel.Text(array[num], _label); _libraryDraft[num] = Panel.Input("library-setting-" + num, _libraryDraft[num], 10); } Panel.Text("Archive capacity is separate from the maximum pieces allowed when placing a blueprint. Lower limits do not delete existing valid designs.", _hint); if (Panel.Button("Save and apply on server")) { action = delegate { int[] array3 = _libraryDraft.Select(int.Parse).ToArray(); LibraryConfiguration libraryConfiguration = new LibraryConfiguration { Enabled = _shareEnabled, AdminUploadsOnly = _adminUploads, FileMiB = array3[0], TotalMiB = array3[1], MaximumFiles = array3[2], FilesPerUser = array3[3], MaximumPieces = array3[4], CleanupSeconds = array3[5] }; libraryConfiguration.Validate(); library.SaveSettings(libraryConfiguration, _settingsRevision); _librarySettings = false; }; } if (Panel.Button("Back to library · discard edits")) { action = delegate { _librarySettings = false; }; } return; } if (_libraryUploads) { Panel.BeginToolbar(); Panel.Text("Upload a local BBP · the original file stays on your computer", _ready); Panel.Text("Save your selection first, or import a PlanBuild file and save a separate .bbp copy.", _hint, 16); FileHeader(); Panel.EndToolbar(); int count = _uploadPage * 12; foreach (string path in _uploadPaths.Skip(count).Take(12)) { if (FileRow(path, count++, "Upload", input && library.UploadAllowed)) { action = delegate { library.Upload(path); _libraryUploads = false; }; } } Panel.Enabled = input; if (_uploadPaths.Length == 0) { Panel.Text("No saved .bbp files found.", _label); } return; } if (_libraryDownload != null) { Panel.Text("Download: " + LibraryStore.Label(_libraryDownload.Name), _label); Panel.Text("Local filename (.bbp added automatically). Existing files are never overwritten.", _hint); _downloadName = Panel.Input("library-download-name", _downloadName, 80); if (Panel.Button("Download and save locally")) { action = delegate { library.Download(_libraryDownload, _downloadName); _libraryDownload = null; }; } if (Panel.Button("Back to library")) { action = delegate { _libraryDownload = null; }; } return; } if (_libraryDelete != null) { Panel.Text("Remove this blueprint from the server library?\n" + LibraryStore.Label(_libraryDelete.Name), _warning); Panel.Text("Already downloaded copies are unaffected.", _hint); if (Panel.Button("Confirm removal")) { action = delegate { library.Delete(_libraryDelete); _libraryDelete = null; }; } if (Panel.Button("Keep blueprint")) { action = delegate { _libraryDelete = null; }; } return; } Panel.BeginToolbar(); Panel.Text(library.Status, _ready, 16); if (library.Notice.Length > 0) { Panel.Text(library.Notice, _hint, 16); } Panel.BeginColumns(40f, LibrarySearchColumns); _librarySearch = Panel.Input("Search shared blueprints", _librarySearch, 80); if (Panel.Button("Search / refresh library")) { action = delegate { library.Browse(0, _librarySearch); Panel.ResetScroll(); }; } Panel.Enabled = input && library.UploadAllowed; if (Panel.Button("Upload a saved blueprint")) { action = delegate { _uploadPaths = (from p in BlueprintFiles.List() where string.Equals(Path.GetExtension(p), ".bbp", StringComparison.OrdinalIgnoreCase) select p).ToArray(); _libraryUploads = true; _uploadPage = 0; _fileDetails.Clear(); Panel.ResetScroll(); }; } Panel.EndColumns(); Panel.Enabled = input; Panel.Text(library.IsAdmin ? "Administrator access confirmed by the server." : "No administrator access reported by the server. Status refreshes automatically.", _hint, 16); if (library.IsAdmin) { Panel.BeginColumns(38f, AdminColumns); if (Panel.Button("Server settings · admin")) { action = EditLibrarySettings; } if (Panel.Button("Validate all files on server · admin")) { action = library.Scan; } Panel.EndColumns(); } Panel.Text($"Archive limits: {page.LimitPieces:N0} pieces · {page.LimitBytes / 1048576} MiB per file", _hint, 16); Panel.BeginColumns(32f, LibraryColumns); string[] array2 = new string[6] { "BLUEPRINT", "PIECES", "FILE SIZE", "UPLOADER", "DOWNLOAD", "REMOVE" }; foreach (string text in array2) { Panel.Text(text, BuildPanelTheme.Title, 16); } Panel.EndColumns(); Panel.EndToolbar(); int num3 = 0; LibraryEntry[] entries = page.Entries; foreach (LibraryEntry entry in entries) { Panel.BeginColumns(46f, LibraryColumns, num3++); Panel.Text(LibraryStore.Label(entry.Name)); Panel.Text($"{entry.Pieces:N0}", _hint, 16); Panel.Text(FileSize(entry.Bytes), _hint, 16); Panel.Text(entry.Mine ? "You" : "Shared", _hint, 16); Panel.Enabled = input && page.Configuration.Enabled; if (Panel.ButtonKey("Download", "download:" + entry.Id)) { action = delegate { _libraryDownload = entry; string text2 = new string((from c in LibraryStore.Label(entry.Name) select (!Path.GetInvalidFileNameChars().Contains(c)) ? c : '_').Take(60).ToArray()); _downloadName = (string.IsNullOrWhiteSpace(text2) ? ("Shared-" + entry.Id.Substring(0, 8)) : (text2 + " (shared)")); Panel.ResetScroll(); }; } Panel.Enabled = input && (entry.Mine || library.IsAdmin); if (Panel.ButtonKey("Remove", "remove:" + entry.Id)) { action = delegate { _libraryDelete = entry; }; } Panel.EndColumns(); Panel.Enabled = input; } if (page.Entries.Length == 0) { Panel.Text("No shared blueprints match this view.", _hint); } } private static void DrawLibraryFooter(bool input, ref Action action) { BlueprintLibrary library = Plugin.Instance.Library; if (!library.Available || library.Busy || library.Page == null) { return; } LibraryPacket page = library.Page; int next = PageButtons(_libraryUploads ? (_uploadPage * 12) : page.Offset, _libraryUploads ? _uploadPaths.Length : page.Total, input); if (next >= 0) { action = delegate { if (_libraryUploads) { _uploadPage = next / 12; } else { library.Browse(next, _librarySearch); } Panel.ResetScroll(); }; } if (_libraryUploads && Panel.Button("Back to server library")) { action = delegate { _libraryUploads = false; Panel.ResetScroll(); }; } } internal static void OpenTerrain() { OpenControls(); _terrain = true; _controls = false; _terrainChecked = false; } private static void OpenSave() { _fileName = Plugin.Instance.Blueprint.Data?.name ?? "My building"; _saving = true; _controls = false; _overwrite = false; _error = (_notice = ""); } internal static void Open(BlueprintProject project) { Close(); Project = project; _error = ""; } internal static void OpenControls() { BlueprintSelection blueprintSelection = Plugin.Instance?.Blueprint; if (blueprintSelection != null) { Close(); blueprintSelection.PauseForControls(); _controls = true; _openedFrame = Time.frameCount; } } internal static void OpenManager() { Close(); Plugin.Instance?.Blueprint?.PauseForControls(); ManagementOpen = true; _controls = true; _openedFrame = Time.frameCount; } private static void OpenFiles() { string[] paths = BlueprintFiles.List(); _files = true; _controls = false; _paths = paths; _filePage = 0; _filteredSearch = null; _fileDetails.Clear(); Panel.ResetScroll(); _notice = ""; } internal static void Close() { CloseLibrary(); if (BlueprintBuilder.Active != null) { BlueprintBuilder.Active.CancelRequested = true; } if (Object.op_Implicit((Object)(object)Project) && Object.op_Implicit((Object)(object)Project.View) && Project.View.IsOwner()) { Project.Container.SetInUse(false); } _import = null; Project = null; ManagementOpen = false; _files = (_controls = (_saving = (_terrain = (_terrainChecked = (_overwrite = false))))); _confirmCancel = false; Panel.ResetScroll(); _error = (_notice = ""); Panel.Hide(); Plugin.Instance?.Blueprint?.BlockInput(); Plugin.Instance?.RemovalInput.Block(); } internal static void Tick() { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (!Visible || SettingsMenu.Visible) { return; } Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || (!ManagementOpen && !Plugin.Instance.CanBlueprint)) { Close(); return; } if (ControlsVisible && Plugin.Instance.Blueprint == null && !ManagementOpen) { Close(); return; } if (!Application.isFocused) { if (Object.op_Implicit((Object)(object)Project)) { Close(); } return; } BlueprintSelection blueprint = Plugin.Instance.Blueprint; if (blueprint != null && blueprint.PauseInputForFocus()) { return; } if (Time.frameCount != _openedFrame && (BuildingControls.Cancel.Down || BuildingControls.Menu.Down)) { if (_files || _saving || _terrain || _import != null || _library) { BackToControls(); } else { Close(); } return; } _import?.Tick(); if (Object.op_Implicit((Object)(object)Project) && (!Project.View.IsOwner() || Vector3.Distance(((Component)localPlayer).transform.position, ((Component)Project).transform.position) > 5f || (!Project.Administrator(localPlayer) && !PrivateArea.CheckAccess(((Component)Project).transform.position, 0f, false, false)))) { Close(); } } internal static void Draw() { //IL_0f01: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0f34: Unknown result type (might be due to invalid IL or missing references) //IL_0f4f: Unknown result type (might be due to invalid IL or missing references) //IL_12b9: Unknown result type (might be due to invalid IL or missing references) //IL_12e1: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0628: Unknown result type (might be due to invalid IL or missing references) //IL_064e: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Unknown result type (might be due to invalid IL or missing references) //IL_057d: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0de3: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_0e18: Unknown result type (might be due to invalid IL or missing references) //IL_0705: Unknown result type (might be due to invalid IL or missing references) //IL_07c9: Unknown result type (might be due to invalid IL or missing references) //IL_0808: Unknown result type (might be due to invalid IL or missing references) //IL_0e51: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Unknown result type (might be due to invalid IL or missing references) //IL_082c: Unknown result type (might be due to invalid IL or missing references) //IL_0454: Unknown result type (might be due to invalid IL or missing references) //IL_0eb7: Unknown result type (might be due to invalid IL or missing references) //IL_08ac: Unknown result type (might be due to invalid IL or missing references) //IL_099f: Unknown result type (might be due to invalid IL or missing references) //IL_0915: Unknown result type (might be due to invalid IL or missing references) //IL_0d44: Unknown result type (might be due to invalid IL or missing references) //IL_0d79: Unknown result type (might be due to invalid IL or missing references) //IL_0da3: Unknown result type (might be due to invalid IL or missing references) if (!Visible || SettingsMenu.Visible) { return; } int num; if (Application.isFocused) { Plugin instance = Plugin.Instance; num = ((instance == null || instance.Blueprint?.InputPaused != true) ? 1 : 0); } else { num = 0; } bool flag = (byte)num != 0; Color label = _label; string title = (_library ? "Server blueprint library" : ((_import == null) ? (_saving ? "Save blueprint" : (_terrain ? "Blueprint terrain" : (_controls ? "Blueprint controls" : (_files ? "Local blueprint library" : (Project.Data?.name ?? "Construction project"))))) : (_import.Native ? "Check blueprint assets" : "Import PlanBuild blueprint"))); bool flag2 = _files || LibraryTableVisible; if (!(flag2 ? Panel.BeginLarge(title) : Panel.Begin(title, 620f, 710f))) { return; } Panel.Enabled = flag; Action action = null; try { int num2; if (_controls) { Plugin instance2 = Plugin.Instance; num2 = ((instance2 != null && instance2.Blueprint?.OverPlacementLimit == true) ? 1 : 0); } else { num2 = 0; } bool flag3 = (byte)num2 != 0; BuildingPanel panel; string text; Color value; if (!flag2) { Panel.Space(10f); panel = Panel; text = (_library ? "Share designs with this server" : ((_import == null) ? (flag3 ? "Save-only selection" : (_controls ? "Select, save or position a blueprint" : (_saving ? "Choose a file name" : (_terrain ? "Choose the ground for this building" : (_files ? "Choose a saved building" : "Contribute materials and build"))))) : ((!_import.Complete) ? "Reading and checking pieces..." : (_import.CanImport ? "Review before importing" : "Import blocked")))); if (!flag3) { PlanBuildImport import = _import; if (import == null || !import.Complete || _import.CanImport) { value = _ready; goto IL_023e; } } value = _warning; goto IL_023e; } goto IL_0259; IL_0259: if (_library) { DrawLibrary(flag, ref action); } else if (_import != null) { PlanBuildImport import2 = _import; if (import2.Data != null) { Panel.Text(import2.Data.name + $"\nPieces: {import2.Data.pieces.Length:N0} · Checked: {import2.Processed:N0} / {import2.SourceCount:N0} source entries", label); } Panel.Text("Imports buildings, natural objects, item stand decorations (quality 1) and sign text. Natural objects have no material cost. Container contents, portal links, owners and other gameplay state are never copied. Building recipes and placement permissions are checked by BetterBuilding.", _hint); Panel.Text("Missing and unsupported prefabs are skipped and listed below. The remaining pieces can be imported; the source file stays unchanged.", _hint); Panel.Space(10f); foreach (KeyValuePair<string, int> warning in import2.Warnings) { Panel.Text($"{warning.Value:N0} × {warning.Key}", _warning); } foreach (KeyValuePair<string, int> error in import2.Errors) { Panel.Text($"{error.Value:N0} × {error.Key}", _warning); } Panel.Text(import2.Native ? "The original file stays unchanged. Accept to use the available parts; save with a new name to keep a separate copy." : "Creator, description, categories and thumbnails are not retained. The original file stays unchanged; use Save blueprint to create a .bbp copy.", _hint); bool flag4 = import2.Data != null && import2.Data.pieces.Length > Plugin.Instance.Settings.MaximumBlueprint; if (flag4) { Panel.Text("Above this server's placement limit. Importing and saving are allowed; placing is disabled.", _warning); } Panel.Enabled = flag && import2.CanImport; if (Panel.Button(flag4 ? "Accept for saving" : (import2.Native ? "Accept available parts and position blueprint" : "Accept import and position blueprint"))) { action = AcceptImport; } Panel.Enabled = flag; } else if (_saving) { Panel.Text("File name (.bbp is added automatically)", label); string text2 = Panel.Input("Blueprint file name", _fileName, 85); if (text2 != _fileName) { _fileName = text2; _overwrite = false; _error = ""; } Panel.Space(10f); Panel.Text("Folder: " + BlueprintFiles.DirectoryPath, _hint); DrawCaptureOption(); if (_overwrite) { Panel.Text("A blueprint with this name already exists. Replace it?", _warning); } if (Panel.Button(_overwrite ? "Confirm overwrite" : "Save")) { action = Save; } } else if (_terrain) { BlueprintSelection session = Plugin.Instance.Blueprint; Panel.Text($"Terrain radius: {session.Data.terrain.radius:0.#} m\nBlending border: {4f:0.#} m\nThe saved ground follows the blueprint's rotation and height.", label); Panel.Space(12f); Panel.Text("Adapting terrain changes the world immediately. Cancelling the building afterwards will not restore the old ground. Structural stability remains your responsibility.", _hint); if (Panel.Button("Keep existing ground and place")) { action = delegate { session.ChooseTerrain(adapt: false); }; } Panel.Enabled = flag && session.CanUseTerrain; if (Panel.Button(_terrainChecked ? "Confirm: adapt terrain and place" : "Check terrain adaptation")) { action = (_terrainChecked ? ((Action)delegate { session.ChooseTerrain(adapt: true); }) : ((Action)delegate { session.CheckTerrain(); _terrainChecked = true; _notice = "The terrain fits the native height limits. Confirm to apply."; })); } Panel.Enabled = flag; if (!session.CanUseTerrain) { Panel.Text("Terrain adaptation is unavailable with the current position, preview or server settings. The existing ground can still be used.", _warning); } } else if (_files) { DrawLocalFiles(flag, ref action); } else if (_controls) { BlueprintSelection blueprintSelection = Plugin.Instance?.Blueprint; BlueprintClipboard clipboard = Plugin.Instance.Clipboard; bool flag5 = blueprintSelection?.Data != null; Panel.Text(flag5 ? (blueprintSelection.Data.name + "\n" + blueprintSelection.Data.pieces.Length + " pieces ready to position") : ((blueprintSelection?.SelectedCount ?? 0) + " pieces selected"), label); Panel.Text($"Local selection capacity: {Plugin.Instance.BlueprintSelectionLimit:N0}\nPlacement limit here: {Plugin.Instance.Settings.MaximumBlueprint:N0}", label); if (blueprintSelection != null && blueprintSelection.OverPlacementLimit) { Panel.Text("Above the placement limit. Saving is allowed; copying and placing are disabled.", _warning); } Panel.Space(8f); if (Panel.Checkbox("Keep blueprint after placement", Plugin.Instance.KeepBlueprintAfterPlacement?.Value ?? false)) { action = delegate { Plugin.Instance.KeepBlueprintAfterPlacement.Value = !Plugin.Instance.KeepBlueprintAfterPlacement.Value; }; } if (!flag5 && blueprintSelection != null) { Panel.Text("Include in selection", _hint); if (Panel.Checkbox("Natural objects (trees, rocks, plants)", blueprintSelection.IncludeNature)) { action = blueprintSelection.ToggleNature; } if (Panel.Checkbox("Vehicles (carts and boats)", blueprintSelection.IncludeVehicles)) { action = blueprintSelection.ToggleVehicles; } Panel.Text("Unchecking removes these objects from the current selection.", _hint, 16); Panel.Space(8f); } if (Panel.Button("Local blueprint library")) { action = OpenFiles; } if (Panel.Button("Server blueprint library")) { action = OpenLibrary; } if (blueprintSelection == null) { Panel.Text("Browse, upload and download without a hammer. Equip a hammer to select or position buildings.", _hint); } Panel.Enabled = flag && blueprintSelection != null; if (Panel.Button(flag5 ? "Continue positioning" : "Continue selecting")) { action = Close; } Panel.Enabled = flag && (blueprintSelection?.CanSave ?? false); if (Panel.Button(flag5 ? "Save blueprint" : "Save selection as blueprint")) { action = OpenSave; } DrawCaptureOption(); if (flag5) { Panel.Enabled = flag && blueprintSelection.CanLockPosition; if (Panel.Button((blueprintSelection.PositionLocked ? "Release preview position" : "Lock preview position") + $" ({BuildingControls.PausePreview})")) { action = blueprintSelection.TogglePositionLock; } } Panel.Enabled = flag && (blueprintSelection?.CanCopy ?? false); if (Panel.Button($"Copy ({BuildingControls.Copy})")) { action = CopySelection; } Panel.Enabled = flag && blueprintSelection != null && clipboard.HasData && clipboard.Count <= blueprintSelection.PlacementLimit && clipboard.Count <= blueprintSelection.SelectionLimit; if (Panel.Button($"Paste ({BuildingControls.Paste})")) { action = Paste; } Panel.Enabled = flag; Panel.Enabled = flag && CanPositionBlueprint; if (Panel.Button("New selection")) { action = delegate { Plugin.Instance.StartBlueprint(); if (Plugin.Instance.Blueprint != null) { Close(); } }; } Panel.Enabled = flag && blueprintSelection != null; if (!flag5 && blueprintSelection != null && Panel.Button("Area selection: " + (blueprintSelection.AreaSelection ? "ON" : "OFF") + $" ({BuildingControls.Area})")) { action = blueprintSelection.ToggleArea; } if (flag5 && Panel.Button("Anchor snapping: " + (blueprintSelection.Snapping ? "ON" : "OFF"))) { action = blueprintSelection.ToggleSnapping; } Panel.Enabled = flag; if (Panel.Button((Plugin.Instance.FreeCamera.Active ? "Return to player" : "Detach camera") + $" ({BuildingControls.Camera})")) { action = Plugin.Instance.FreeCamera.Toggle; } if (Panel.Button("Settings")) { action = SettingsMenu.Open; } Panel.Space(12f); Panel.Text(clipboard.HasData ? $"Clipboard: {clipboard.Count:N0} pieces" : "Clipboard empty — copy a selection first", _hint); Panel.Space(8f); Panel.Text(flag5 ? BuildingControls.PreviewHint : BuildingControls.SelectionHint, _hint); Panel.Space(8f); Panel.Text(BuildingControls.NavigationHint, _hint); } else if (Object.op_Implicit((Object)(object)Project)) { if (Project.Administrator(Player.m_localPlayer)) { Panel.Text("Server administrator: project recovery is available, including projects created by other players. Cancel returns materials at the chest.", _warning); } Panel.Text(BlueprintBuilder.Active?.Status ?? Project.Problem, label); BuildingPanel panel2 = Panel; BlueprintData data = Project.Data; panel2.Text($"Pieces: {((data != null) ? data.pieces.Length : 0)}\nMaterials: delivered / required", label); try { foreach (KeyValuePair<string, int> item in Project.Remaining()) { Panel.Text($"{BlueprintWorld.Label(item.Key)}: {Project.Received(item.Key)} / {item.Value}", label); } } catch (Exception ex) { _error = ex.Message; } if (Project.HasDisplayItems) { Panel.Text("Display items: bring unequipped quality-1 items, default style, fully repaired and without custom item data.", _hint); } Panel.Text(Plugin.Instance.Settings.ClientOnly ? "Client-only project: materials are spent per placed piece. Disconnects may leave incomplete parts that collapse. No rollback or recovery of spent materials." : "Materials stay reserved until the server confirms completion. Retry construction clears only this attempt; terrain changes are not undone.", _hint); Panel.Text("Structural stability is your responsibility.\nNo stamina or hammer durability costs.", _hint); } Panel.EndScroll(); if (_files) { DrawLocalFooter(flag, ref action); } if (LibraryTableVisible) { DrawLibraryFooter(flag, ref action); } if (Object.op_Implicit((Object)(object)Project)) { bool flag6 = BlueprintBuilder.Active != null || Project.RecoveryBusy; bool flag7 = false; try { flag7 = Project.NeedsMaterials; } catch { } Panel.Enabled = flag && !flag6 && !Project.Recoverable && Project.Locked && flag7; if (Panel.Button("Deposit required materials")) { Run(Project.Deposit); } bool flag8 = Project.CanManage(Player.m_localPlayer); Panel.Enabled = flag && !flag6 && flag8 && Project.Locked; if (Panel.Button(Project.Shared ? "Contributions: allowed — click to restrict" : "Contributions: creator only — click to allow")) { Project.View.GetZDO().Set("vdb_shared", !Project.Shared); } bool flag9 = false; try { flag9 = Project.Funded(); } catch { } BuildingPanel panel3 = Panel; int enabled; if (flag && !flag6 && !Project.Recoverable && flag8 && Project.Locked && flag9) { int progress = Project.Progress; BlueprintData data2 = Project.Data; enabled = ((progress < ((data2 != null) ? data2.pieces.Length : 0)) ? 1 : 0); } else { enabled = 0; } panel3.Enabled = (byte)enabled != 0; if (Panel.Button("Build")) { Run(Project.Build); } if (Project.Recoverable) { bool flag10 = false; try { flag10 = Project.Recovery.Committed; } catch { } Panel.Enabled = flag && !flag6 && flag8 && Project.Locked && !flag10; if (Panel.Button("Retry construction — clear attempt, keep materials")) { Run(Project.Retry); } BuildingPanel panel4 = Panel; int enabled2; if (flag && !flag6 && flag8 && Project.Locked) { int progress2 = Project.Progress; BlueprintData data3 = Project.Data; enabled2 = ((progress2 == ((data3 != null) ? data3.pieces.Length : (-1))) ? 1 : 0); } else { enabled2 = 0; } panel4.Enabled = (byte)enabled2 != 0; if (Panel.Button("Finish construction / confirm with server")) { Run(delegate { Project.Finish(Player.m_localPlayer); }); } } Panel.Enabled = flag && !flag6 && flag8 && Project.Locked; if (Panel.Button(_confirmCancel ? "Confirm: remove this attempt and return available materials" : "Cancel / close project and return materials")) { if (_confirmCancel) { Run(Project.ReturnAndRemove); } else { _confirmCancel = true; } } } Panel.Enabled = flag; if (_notice.Length > 0) { Panel.Text(_notice, _ready); } if (_error.Length > 0) { Panel.Text(_error, _warning); } if (flag2) { Panel.BeginColumns(38f, (!ManagementOpen) ? new float[2] { 0.5f, 0.5f } : new float[3] { 0.34f, 0.33f, 0.33f }); } if ((_files || _saving || _terrain || _import != null || _library) && Panel.Button("Back to controls")) { action = BackToControls; } if (ManagementOpen && Panel.Button("Back to settings")) { action = delegate { Close(); SettingsMenu.Open(); }; } if (Panel.Button(_controls ? $"Deactivate ({BuildingControls.Blueprint})" : ((BlueprintBuilder.Active != null) ? "Stop construction and close" : "Close"))) { action = (_controls ? ((Action)delegate { Plugin.Instance.StopBlueprint(); }) : new Action(Close)); } if (flag2) { Panel.EndColumns(); } Panel.End(); goto end_IL_012b; IL_023e: panel.Text(text, value); Panel.Space(12f); goto IL_0259; end_IL_012b:; } finally { Panel.Enabled = true; } if (action != null) { Run(action); } } private static void BackToControls() { CloseLibrary(); _import = null; _files = (_saving = (_terrain = (_terrainChecked = (_overwrite = false)))); _controls = true; Panel.ResetScroll(); _error = (_notice = ""); _openedFrame = Time.frameCount; } private static void DrawCaptureOption() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) BlueprintSelection blueprint = Plugin.Instance.Blueprint; if (blueprint != null) { if (blueprint.Data != null) { Panel.Text((blueprint.Data.terrain == null) ? "No terrain profile in this blueprint." : "Includes the original terrain profile.", _hint); } else if (Panel.Button("Include terrain when copying/saving: " + (blueprint.IncludeTerrain ? "ON" : "OFF"))) { blueprint.IncludeTerrain = !blueprint.IncludeTerrain; } } } private static void CopySelection() { BlueprintSelection blueprint = Plugin.Instance.Blueprint; if (blueprint == null || !blueprint.Copy()) { throw new InvalidOperationException(blueprint?.Problem ?? "Select valid pieces before copying."); } _notice = "Copied " + Plugin.Instance.Clipboard.Count + " pieces. Choose Paste blueprint to position the copy."; } private static void Paste() { BlueprintSelection blueprint = Plugin.Instance.Blueprint; if (blueprint == null || !blueprint.Paste()) { throw new InvalidOperationException(blueprint?.Problem ?? "Activate blueprint mode first."); } Close(); } private static void Save() { if (File.Exists(BlueprintFileName.PathIn(BlueprintFiles.DirectoryPath, _fileName)) && !_overwrite) { _overwrite = true; return; } string path = Plugin.Instance.Blueprint.Save(_fileName, _overwrite); BackToControls(); _notice = "Saved " + Path.GetFileName(path); } private static void Load(string path) { if (!CanPositionBlueprint) { throw new InvalidOperationException("Equip a hammer to position this blueprint."); } if (BlueprintFiles.IsPlanBuild(path)) { _import = new PlanBuildImport(path, Plugin.Instance.BlueprintSelectionLimit); _files = (_controls = false); Panel.ResetScroll(); _error = (_notice = ""); _openedFrame = Time.frameCount; } else { _import = new PlanBuildImport(BlueprintFiles.Load(path, Plugin.Instance.BlueprintSelectionLimit), Plugin.Instance.BlueprintSelectionLimit); _files = (_controls = false); Panel.ResetScroll(); _error = (_notice = ""); _openedFrame = Time.frameCount; } } private static void AcceptImport() { PlanBuildImport import = _import; if (import != null && import.CanImport) { BlueprintData data = _import.Data; data.Validate(Plugin.Instance.BlueprintSelectionLimit); Plugin.Instance.StartBlueprint(data); if (Plugin.Instance.Blueprint?.Data != data) { throw new InvalidOperationException("Blueprint mode is unavailable."); } Close(); if (Plugin.Instance.Blueprint.OverPlacementLimit) { OpenControls(); } } } private static void Run(Action action) { try { action(); _error = ""; } catch (Exception ex) { _error = ex.Message; Plugin.Instance.LogNetwork(ex.ToString()); } } } internal static class BlueprintFileName { internal static string Stem(string name) { name = (name ?? "").Trim(); if (name.EndsWith(".bbp", StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, name.Length - 4); } else if (name.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, name.Length - 5); } if (name.Length == 0 || name.Length > 80 || name.EndsWith(".") || name.EndsWith(" ") || name.IndexOfAny("<>:\"/\\|?*".ToCharArray()) >= 0 || name == "." || name == "..") { throw new ArgumentException("Choose a file name of 1–80 characters, without paths or special file characters."); } string text = name; for (int i = 0; i < text.Length; i++) { if (char.IsControl(text[i])) { throw new ArgumentException("Control characters are not allowed in file names."); } } string text2 = name.Split('.')[0].ToUpperInvariant(); switch (text2) { default: if (text2.Length != 4 || (!text2.StartsWith("COM") && !text2.StartsWith("LPT")) || "123456789¹²³".IndexOf(text2[3]) < 0) { break; } goto case "CON"; case "CON": case "PRN": case "AUX": case "NUL": throw new ArgumentException("That file name is reserved by Windows. Choose another name."); } return name; } internal static string PathIn(string directory, string name) { return Path.Combine(Path.GetFullPath(directory), Stem(name) + ".bbp"); } } internal static class BlueprintFiles { private sealed class PieceArrayReader : JsonConverter { public override bool CanWrite => false; public override bool CanConvert(Type type) { return type == typeof(BlueprintPiece[]); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); } public override object ReadJson(JsonReader reader, Type type, object existing, JsonSerializer serializer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 if ((int)reader.TokenType != 2) { throw new ArgumentException("Invalid blueprint pieces."); } List<BlueprintPiece> list = new List<BlueprintPiece>(); while (reader.Read()) { if ((int)reader.TokenType == 14) { return list.ToArray(); } if (list.Count >= 250000) { throw new ArgumentException("The blueprint exceeds the supported piece limit."); } list.Add(serializer.Deserialize<BlueprintPiece>(reader)); } throw new ArgumentException("Incomplete blueprint pieces."); } } private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings { TypeNameHandling = (TypeNameHandling)0, MetadataPropertyHandling = (MetadataPropertyHandling)2, NullValueHandling = (NullValueHandling)1, MaxDepth = 32, Converters = { (JsonConverter)(object)new PieceArrayReader() } }; internal static string DirectoryPath => Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location), "Blueprints"); private static void PrepareDirectory() { Directory.CreateDirectory(DirectoryPath); } internal static string Encode(BlueprintData data) { data.Validate(); string text = JsonConvert.SerializeObject((object)data, (Formatting)1, JsonSettings); if (Encoding.UTF8.GetByteCount(text) > 67108864) { throw new ArgumentException("Blueprint file is too large."); } return text; } internal static BlueprintData Decode(string json, int maximum) { if (json == null || Encoding.UTF8.GetByteCount(json) > 67108864) { throw new ArgumentException("Blueprint file is too large."); } BlueprintData obj = JsonConvert.DeserializeObject<BlueprintData>(json, JsonSettings) ?? throw new ArgumentException("Invalid blueprint file."); obj.Validate(maximum); return obj; } internal static string Save(BlueprintData data, string fileName, bool overwrite = false) { string name = BlueprintFileName.Stem(fileName); PrepareDirectory(); string text = BlueprintFileName.PathIn(DirectoryPath, name); byte[] bytes = BlueprintFileCodec.Encode(new BlueprintData { formatVersion = data.formatVersion, name = name, pieces = data.pieces, terrain = data.terrain }); string text2 = Path.Combine(DirectoryPath, Guid.NewGuid().ToString("N") + ".tmp"); try { File.WriteAllBytes(text2, bytes); if (overwrite && File.Exists(text)) { File.Replace(text2, text, null); } else { File.Move(text2, text); } data.name = name; return text; } finally { if (File.Exists(text2)) { File.Delete(text2); } } } internal static string[] List() { PrepareDirectory(); return (from path in Directory.GetFiles(DirectoryPath, "*", SearchOption.TopDirectoryOnly) where string.Equals(Path.GetExtension(path), ".bbp", StringComparison.OrdinalIgnoreCase) || string.Equals(Path.GetExtension(path), ".json", StringComparison.OrdinalIgnoreCase) || IsPlanBuild(path) select path).OrderBy<string, string>(Path.GetFileName, StringComparer.OrdinalIgnoreCase).ToArray(); } internal static bool IsPlanBuild(string path) { if (!string.Equals(Path.GetExtension(path), ".blueprint", StringComparison.OrdinalIgnoreCase)) { return string.Equals(Path.GetExtension(path), ".vbuild", StringComparison.OrdinalIgnoreCase); } return true; } internal static PlanBuildDocument ReadPlanBuild(string path, int maximum) { string fullPath = Path.GetFullPath(path); if (!string.Equals(Path.GetDirectoryName(fullPath), Path.GetFullPath(DirectoryPath), StringComparison.OrdinalIgnoreCase) || !IsPlanBuild(fullPath)) { throw new ArgumentException("Choose a .blueprint or .vbuild file from the blueprint folder."); } using FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read); if (fileStream.Length > 67108864) { throw new ArgumentException("PlanBuild file is too large."); } using StreamReader reader = new StreamReader(fileStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true); return PlanBuildParser.Read(reader, string.Equals(Path.GetExtension(fullPath), ".vbuild", StringComparison.OrdinalIgnoreCase), Path.GetFileNameWithoutExtension(fullPath), maximum); } internal static BlueprintData Load(string path, int maximum) { string fullPath = Path.GetFullPath(path); if (!string.Equals(Path.GetDirectoryName(fullPath), Path.GetFullPath(DirectoryPath), StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Choose a file from the blueprint folder."); } bool flag = string.Equals(Path.GetExtension(fullPath), ".bbp", StringComparison.OrdinalIgnoreCase); if (!flag && !string.Equals(Path.GetExtension(fullPath), ".json", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Choose a .bbp blueprint or a legacy .json file."); } using FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read); int num = (flag ? 67108864 : 67108864); if (fileStream.Length > num) { throw new ArgumentException("Blueprint file is too large."); } BlueprintData blueprintData; if (flag) { using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8, leaveOpen: true); blueprintData = BlueprintFileCodec.Decode(binaryReader.ReadBytes(checked((int)fileStream.Length)), maximum); } else { using StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); blueprintData = Decode(streamReader.ReadToEnd(), maximum); } blueprintData.pieces = blueprintData.pieces.OrderBy((BlueprintPiece piece) => piece.y).ToArray(); return blueprintData; } } internal static class BlueprintFreshPieces { internal const string PendingPortalKey = "vdb_unconfigured_portal"; internal static ZDO Pending(TeleportWorld portal) { ZNetView component = ((Component)portal).GetComponent<ZNetView>(); ZDO val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null); if (val == null || !val.GetBool("vdb_unconfigured_portal", false)) { return null; } return val; } } [HarmonyPatch(typeof(TeleportWorld), "Awake")] internal static class BlueprintFreshPortal { private static void Postfix(TeleportWorld __instance) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (BlueprintProbe.Placing) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); zDO.Set(ZDOVars.s_tag, "vdb-unconfigured-" + Guid.NewGuid().ToString("N")); zDO.Set("vdb_unconfigured_portal", true); zDO.SetConnection((ConnectionType)1, ZDOID.None); } } } } [HarmonyPatch(typeof(TeleportWorld), "GetText")] internal static class BlueprintPortalText { private static void Postfix(TeleportWorld __instance, ref string __result) { if (BlueprintFreshPieces.Pending(__instance) != null) { __result = ""; } } } [HarmonyPatch(typeof(TeleportWorld), "GetHoverText")] internal static class BlueprintPortalHover { private static void Postfix(TeleportWorld __instance, ref string __result) { ZDO val = BlueprintFreshPieces.Pending(__instance); if (val != null && val.GetString(ZDOVars.s_tag, "").Length > 0) { __result = __result.Replace(val.GetString(ZDOVars.s_tag, ""), "Not configured"); } } } [HarmonyPatch(typeof(TeleportWorld), "RPC_SetTag")] internal static class BlueprintPortalConfigure { private static void Prefix(TeleportWorld __instance) { ZDO val = BlueprintFreshPieces.Pending(__instance); if (val != null && ((Component)__instance).GetComponent<ZNetView>().IsOwner()) { val.Set("vdb_unconfigured_portal", false); } } } internal static class BlueprintHammerEntry { private static GameObject _root; private static PieceTable _table; internal static Piece Entry; internal static bool IsHammer(ItemData item) { if (Object.op_Implicit((Object)(object)_table)) { return (Object)(object)item?.m_shared.m_buildPieces == (Object)(object)_table; } return false; } internal static void Ensure() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab("Hammer") : null); PieceTable val2 = ((!Object.op_