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 SaveStructure v0.1.0
SaveStructure.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SaveStructure.Core; using Splatform; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SaveStructure")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Select, save, and rebuild named structures with the Valheim hammer.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("SaveStructure")] [assembly: AssemblyTitle("SaveStructure")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 SaveStructure { [BepInPlugin("dylan.valheim.savestructure", "SaveStructure", "0.1.0")] public sealed class SaveStructurePlugin : BaseUnityPlugin { public const string PluginId = "dylan.valheim.savestructure"; public const string PluginVersion = "0.1.0"; internal ConfigEntry<KeyCode> ToggleKey; internal ConfigEntry<KeyCode> SaveKey; internal ConfigEntry<KeyCode> ClearKey; internal ConfigEntry<int> MaxPieces; internal ConfigEntry<float> SelectionDistance; private Harmony harmony; private GUIStyle? hintStyle; internal static SaveStructurePlugin Instance { get; private set; } internal BlueprintStore Store { get; private set; } internal StructureCatalog Catalog { get; private set; } internal SelectionSession Selection { get; private set; } private void Awake() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown Instance = this; ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ToggleSaveMode", (KeyCode)289, "Toggle save mode while holding the Hammer."); SaveKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "SaveSelection", (KeyCode)288, "Name and save the selected pieces."); ClearKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ClearSelection", (KeyCode)8, "Clear the current selection."); MaxPieces = ((BaseUnityPlugin)this).Config.Bind<int>("Selection", "MaximumPieces", 500, new ConfigDescription("Maximum pieces per structure.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 2000), Array.Empty<object>())); SelectionDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Selection", "Reach", 25f, new ConfigDescription("Maximum selection distance from the player in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f), Array.Empty<object>())); Store = new BlueprintStore(Path.Combine(Paths.ConfigPath, "SaveStructure", "structures"), Warn); Catalog = new StructureCatalog(this); Selection = new SelectionSession(this); harmony = new Harmony("dylan.valheim.savestructure"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("SaveStructure 0.1.0 loaded for Valheim " + Version.GetVersionString(false) + ".")); } private void Update() { if (!Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Selection.Cancel(); Catalog.Clear(); } else { Catalog.EnsureRegistered(); Selection.CheckPlayer(); } } private void OnGUI() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_017c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || !SelectionSession.HasHammer(localPlayer) || Menu.IsVisible() || InventoryGui.IsVisible()) { return; } if (hintStyle == null) { hintStyle = new GUIStyle(GUI.skin.box) { fontSize = 16, alignment = (TextAnchor)4, wordWrap = true, richText = false }; } string text = (Selection.Active ? $"SAVE MODE — {Selection.Count} selected\nClick: select / deselect | {SaveKey.Value}: name & save\n{ClearKey.Value}: clear | {ToggleKey.Value}: cancel\nFirst selected piece is the placement anchor." : $"SaveStructure | {ToggleKey.Value}: select a structure to save"); if (!Selection.Active) { Piece selectedPiece = localPlayer.GetSelectedPiece(); if (selectedPiece != null && Catalog.TryGet(selectedPiece, out StructureEntry entry)) { text += $"\n{entry.Blueprint.Name} — {entry.Blueprint.Pieces.Count} pieces\nRotate and place with the normal hammer controls."; } } float num = Mathf.Min(590, Screen.width - 24); GUI.Box(new Rect(((float)Screen.width - num) / 2f, 30f, num, (float)(Selection.Active ? 108 : 80)), text, hintStyle); } private void OnDestroy() { Selection?.Dispose(); Catalog?.Dispose(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal void Warn(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); } internal void Error(Exception exception) { ((BaseUnityPlugin)this).Logger.LogError((object)exception); } internal static void Tell(string text) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } } } [HarmonyPatch] internal static class GamePatches { [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static bool UpdatePlacement(Player __instance, bool takeInput) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } return SaveStructurePlugin.Instance.Selection.HandleInput(__instance, takeInput); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static void HideSelectionGhost(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && SaveStructurePlugin.Instance.Selection.Active && Object.op_Implicit((Object)(object)__instance.m_placementGhost)) { __instance.m_placementGhost.SetActive(false); } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static bool TryPlaceStructure(Player __instance, Piece piece, ref bool __result) { if (!SaveStructurePlugin.Instance.Catalog.TryGet(piece, out StructureEntry entry)) { return true; } __result = false; if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { __result = StructureBuilder.TryBuild(__instance, entry); } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] private static void IncludeSavedStructures(PieceTable __instance, HashSet<string> knownRecipies) { SaveStructurePlugin.Instance.Catalog.AddKnownNames(__instance, knownRecipies); } } internal sealed class SelectionSession : IDisposable { private sealed class NameReceiver : TextReceiver { private readonly SelectionSession session; private readonly int generation; public NameReceiver(SelectionSession session, int generation) { this.session = session; this.generation = generation; } public string GetText() { return ""; } public void SetText(string text) { session.Save(text, generation); } } private readonly SaveStructurePlugin plugin; private readonly List<Piece> selected = new List<Piece>(); private readonly Dictionary<Piece, GameObject> highlights = new Dictionary<Piece, GameObject>(); private Material? lineMaterial; private Player? owner; private int generation; internal bool Active { get; private set; } internal int Count => selected.Count; internal SelectionSession(SaveStructurePlugin plugin) { this.plugin = plugin; } internal static bool HasHammer(Player player) { ItemData rightItem = ((Humanoid)player).GetRightItem(); object obj; if (rightItem == null) { obj = null; } else { GameObject dropPrefab = rightItem.m_dropPrefab; obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if ((string?)obj == "Hammer") { return ((Character)player).InPlaceMode(); } return false; } internal void CheckPlayer() { if (Active && (!Object.op_Implicit((Object)(object)owner) || (Object)(object)owner != (Object)(object)Player.m_localPlayer || ((Character)owner).IsDead() || !HasHammer(owner))) { Cancel(); } } internal bool HandleInput(Player player, bool takeInput) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) CheckPlayer(); if (takeInput && HasHammer(player) && ZInput.GetKeyDown(plugin.ToggleKey.Value, true)) { if (Active) { Cancel(); } else { Active = true; owner = player; generation++; Hud.HidePieceSelection(); SaveStructurePlugin.Tell("Save mode: click each piece, then press " + ((object)plugin.SaveKey.Value/*cast due to .constrained prefix*/).ToString() + " to name the structure."); } ResetBufferedActions(player); return false; } if (!Active) { return true; } ResetBufferedActions(player); if (Object.op_Implicit((Object)(object)player.m_placementGhost)) { player.m_placementGhost.SetActive(false); } if (Object.op_Implicit((Object)(object)player.m_placementMarkerInstance)) { player.m_placementMarkerInstance.SetActive(false); } if (!takeInput || Hud.IsPieceSelectionVisible()) { return false; } RemoveDestroyedPieces(); if (ZInput.GetKeyDown(plugin.ClearKey.Value, true)) { Clear(); } else if (ZInput.GetKeyDown(plugin.SaveKey.Value, true)) { PromptName(); } else if (ZInput.GetButtonDown("Attack") || ZInput.GetButtonDown("JoyPlace")) { ToggleTarget(player); } return false; } private static void ResetBufferedActions(Player player) { player.m_placePressedTime = -9999f; player.m_removePressedTime = -9999f; } private void ToggleTarget(Player player) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)GameCamera.instance)) { return; } Transform transform = ((Component)GameCamera.instance).transform; int mask = LayerMask.GetMask(new string[5] { "piece", "piece_nonsolid", "terrain", "Default", "static_solid" }); RaycastHit val = default(RaycastHit); if (!Physics.Raycast(transform.position, transform.forward, ref val, 150f, mask, (QueryTriggerInteraction)1)) { return; } Piece componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>(); if (!Object.op_Implicit((Object)(object)componentInParent) || Vector3.Distance(((Component)player).transform.position, ((RaycastHit)(ref val)).point) > plugin.SelectionDistance.Value) { return; } if (selected.Remove(componentInParent)) { if (highlights.TryGetValue(componentInParent, out GameObject value)) { Object.Destroy((Object)(object)value); } highlights.Remove(componentInParent); return; } if (selected.Count >= plugin.MaxPieces.Value) { SaveStructurePlugin.Tell($"Selection limit: {plugin.MaxPieces.Value} pieces."); return; } try { ValidateSelectable(componentInParent); if (selected.Count > 0 && Vector3.Distance(((Component)selected[0]).transform.position, ((Component)componentInParent).transform.position) > 128f) { throw new InvalidOperationException("Select pieces within 128 metres of the first piece."); } GameObject value2 = MakeHighlight(componentInParent); highlights[componentInParent] = value2; selected.Add(componentInParent); } catch (Exception ex) { SaveStructurePlugin.Tell(ex.Message); } } private void ValidateSelectable(Piece piece) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)piece).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !piece.IsPlacedByPlayer()) { throw new InvalidOperationException("Select a player-built piece."); } if (!PrivateArea.CheckAccess(((Component)piece).transform.position, 0f, true, false)) { throw new InvalidOperationException("This piece is protected by a ward."); } GameObject prefab = ZNetScene.instance.GetPrefab(component.GetZDO().GetPrefab()); if (!Object.op_Implicit((Object)(object)prefab)) { throw new InvalidOperationException("The original piece prefab is unavailable."); } plugin.Catalog.ValidatePrefab(prefab); Vector3 val = ((Component)piece).transform.lossyScale - prefab.transform.localScale; if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { throw new InvalidOperationException("Rescaled pieces are not supported in this version."); } } private void PromptName() { RemoveDestroyedPieces(); if (selected.Count == 0) { SaveStructurePlugin.Tell("Select at least one piece first."); } else if (Object.op_Implicit((Object)(object)TextInput.instance)) { TextInput.instance.RequestText((TextReceiver)(object)new NameReceiver(this, generation), "Name this structure", 60); } } private void Save(string name, int expectedGeneration) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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) CheckPlayer(); if (!Active || expectedGeneration != generation) { return; } try { if (selected.Any((Piece piece) => !Object.op_Implicit((Object)(object)piece))) { throw new InvalidOperationException("A selected piece was removed. Select the structure again."); } string name2 = Blueprint.ValidateName(name); foreach (Piece item in selected) { ValidateSelectable(item); } Vector3 position = ((Component)selected[0]).transform.position; Quaternion q = Quaternion.Euler(0f, ((Component)selected[0]).transform.eulerAngles.y, 0f); Blueprint blueprint = new Blueprint { Name = name2, GameVersion = Version.GetVersionString(false) }; foreach (Piece item2 in selected) { GameObject prefab = ZNetScene.instance.GetPrefab(((Component)item2).GetComponent<ZNetView>().GetZDO().GetPrefab()); blueprint.Pieces.Add(StructureMath.Capture(((Object)prefab).name, ((Component)item2).transform.position.Numerics(), ((Component)item2).transform.rotation.Numerics(), position.Numerics(), q.Numerics())); } blueprint.Validate(plugin.MaxPieces.Value); plugin.Catalog.Resolve(blueprint); plugin.Store.SaveNew(blueprint); Cancel(); try { plugin.Catalog.Register(blueprint); plugin.Catalog.RefreshPlayer(); SaveStructurePlugin.Tell($"Saved '{blueprint.Name}' ({blueprint.Pieces.Count} pieces). Find it in the Hammer menu by searching 'Structure:'."); } catch (Exception exception) { plugin.Error(exception); SaveStructurePlugin.Tell("Structure saved to disk. Rejoin the world to reload the Hammer menu."); } } catch (Exception ex) { plugin.Error(ex); SaveStructurePlugin.Tell("Could not save: " + ex.Message); } } private GameObject MakeHighlight(Piece piece) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Expected O, but got Unknown //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) Bounds bounds = default(Bounds); ((Bounds)(ref bounds))..ctor(((Component)piece).transform.position, Vector3.one * 0.2f); bool flag = true; Renderer[] componentsInChildren = ((Component)piece).GetComponentsInChildren<Renderer>(); foreach (Renderer val in componentsInChildren) { if (val is MeshRenderer || val is SkinnedMeshRenderer) { if (flag) { bounds = val.bounds; flag = false; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } ((Bounds)(ref bounds)).Expand(0.04f); Shader val2 = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color"); if (lineMaterial == null) { lineMaterial = new Material(val2); } GameObject val3 = new GameObject("SaveStructure selection"); val3.transform.SetParent(((Component)piece).transform, true); Vector3 min = ((Bounds)(ref bounds)).min; Vector3 max = ((Bounds)(ref bounds)).max; Vector3[] array = (Vector3[])(object)new Vector3[8] { new Vector3(min.x, min.y, min.z), new Vector3(max.x, min.y, min.z), new Vector3(max.x, min.y, max.z), new Vector3(min.x, min.y, max.z), new Vector3(min.x, max.y, min.z), new Vector3(max.x, max.y, min.z), new Vector3(max.x, max.y, max.z), new Vector3(min.x, max.y, max.z) }; int[] array2 = new int[24] { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; Color val5 = default(Color); for (int j = 0; j < array2.Length; j += 2) { GameObject val4 = new GameObject("edge"); val4.transform.SetParent(val3.transform, false); LineRenderer obj = val4.AddComponent<LineRenderer>(); ((Renderer)obj).sharedMaterial = lineMaterial; obj.useWorldSpace = false; float startWidth = (obj.endWidth = 0.025f); obj.startWidth = startWidth; ((Color)(ref val5))..ctor(0.25f, 1f, 0.8f, 1f); obj.endColor = val5; obj.startColor = val5; obj.positionCount = 2; obj.SetPosition(0, val4.transform.InverseTransformPoint(array[array2[j]])); obj.SetPosition(1, val4.transform.InverseTransformPoint(array[array2[j + 1]])); } return val3; } private void RemoveDestroyedPieces() { Piece[] array = selected.Where((Piece p) => !Object.op_Implicit((Object)(object)p)).ToArray(); foreach (Piece val in array) { selected.Remove(val); if (highlights.TryGetValue(val, out GameObject value) && Object.op_Implicit((Object)(object)value)) { Object.Destroy((Object)(object)value); } highlights.Remove(val); } } private void Clear() { foreach (GameObject value in highlights.Values) { if (Object.op_Implicit((Object)(object)value)) { Object.Destroy((Object)(object)value); } } highlights.Clear(); selected.Clear(); } internal void Cancel() { if (Active || selected.Count != 0) { Active = false; generation++; if (Object.op_Implicit((Object)(object)owner)) { ResetBufferedActions(owner); } owner = null; Clear(); } } public void Dispose() { Cancel(); if (Object.op_Implicit((Object)(object)lineMaterial)) { Object.Destroy((Object)(object)lineMaterial); } } } internal static class VectorConversions { internal static Vector3 Numerics(this Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vector3(v.x, v.y, v.z); } internal static Quaternion Numerics(this Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(q.x, q.y, q.z, q.w); } internal static Vector3 Unity(this PositionData v) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(v.X, v.Y, v.Z); } internal static Quaternion Unity(this RotationData q) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(q.X, q.Y, q.Z, q.W); } } internal static class StructureBuilder { private sealed class BuildPart { internal Piece Piece; internal Vector3 Position; internal Quaternion Rotation; internal Transform Preview; } internal static bool TryBuild(Player player, StructureEntry entry) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) List<GameObject> list = new List<GameObject>(); try { if (!SelectionSession.HasHammer(player) || SaveStructurePlugin.Instance.Selection.Active) { return false; } player.UpdatePlacementGhost(true); if (!Object.op_Implicit((Object)(object)player.m_placementGhost) || !player.m_placementGhost.activeSelf || (int)player.m_placementStatus != 0) { throw new InvalidOperationException("Choose a valid position for the structure's anchor."); } Vector3 origin = player.m_placementGhost.transform.position; Quaternion heading = player.m_placementGhost.transform.rotation; BuildPart[] array = entry.Pieces.Select((Piece piece, int i) => new BuildPart { Piece = piece, Position = origin + heading * entry.Blueprint.Pieces[i].Position.Unity(), Rotation = heading * entry.Blueprint.Pieces[i].Rotation.Unity() }).ToArray(); Transform[] array2 = (from Transform t in (IEnumerable)player.m_placementGhost.transform where !((Component)t).CompareTag("snappoint") select t).ToArray(); for (int num = 0; num < array.Length; num++) { array[num].Preview = array2[num]; } Physics.SyncTransforms(); Validate(player, entry, array); foreach (BuildPart item in array.OrderBy((BuildPart p) => p.Position.y)) { GameObject val = Object.Instantiate<GameObject>(((Component)item.Piece).gameObject, item.Position, item.Rotation); list.Add(val); ZNetView component = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { throw new InvalidOperationException("A building piece failed to initialise on the network."); } val.GetComponent<Piece>().SetCreator(player.GetPlayerID(), ((IUser)PlatformManager.DistributionPlatform.LocalUser).PlatformUserID); PrivateArea component2 = val.GetComponent<PrivateArea>(); if (component2 != null) { component2.Setup(Game.instance.GetPlayerProfile().GetName()); } WearNTear component3 = val.GetComponent<WearNTear>(); if (component3 != null) { component3.OnPlaced(); } foreach (IPlaced item2 in val.GetComponents<MonoBehaviour>().OfType<IPlaced>()) { item2.OnPlaced(); } } } catch (Exception ex) { for (int num2 = list.Count - 1; num2 >= 0; num2--) { try { if (Object.op_Implicit((Object)(object)list[num2])) { ZNetScene.instance.Destroy(list[num2]); } } catch (Exception exception) { SaveStructurePlugin.Instance.Error(exception); } } SaveStructurePlugin.Instance.Warn("Structure placement failed: " + ex.Message); SaveStructurePlugin.Tell(ex.Message); return false; } try { foreach (GameObject item3 in list) { CraftingStation componentInChildren = item3.GetComponentInChildren<CraftingStation>(); if (Object.op_Implicit((Object)(object)componentInChildren)) { player.AddKnownStation(componentInChildren); } } player.FaceLookDirection(); ((Character)player).m_zanim.SetTrigger(((Humanoid)player).GetRightItem().m_shared.m_attack.m_attackAnimation); SaveStructurePlugin.Tell($"Built '{entry.Blueprint.Name}' ({list.Count} pieces)."); } catch (Exception exception2) { SaveStructurePlugin.Instance.Error(exception2); } return true; } private static void Validate(Player player, StructureEntry entry, BuildPart[] plan) { //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Invalid comparison between Unknown and I4 //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Unknown result type (might be due to invalid IL or missing references) bool flag = player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)24); if (!flag && !player.HaveRequirements(entry.Prefab.GetComponent<Piece>(), (RequirementMode)0)) { throw new InvalidOperationException("You need all the structure's materials in your inventory."); } float num = default(float); foreach (BuildPart part in plan) { Piece piece = part.Piece; SaveStructurePlugin.Instance.Catalog.ValidatePrefab(((Component)piece).gameObject); if (!piece.m_enabled) { SeasonalItemGroup currentSeason = player.CurrentSeason; if (currentSeason == null || !currentSeason.Pieces.Contains(((Component)piece).gameObject)) { throw new InvalidOperationException("A piece in this structure is currently disabled: " + Label(piece)); } } if (piece.m_dlc.Length > 0 && !DLCMan.instance.IsDLCInstalled(piece.m_dlc)) { throw new InvalidOperationException("Required DLC is unavailable for " + Label(piece)); } if (!flag && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)26) && !player.HaveRequirements(piece, (RequirementMode)1)) { throw new InvalidOperationException("Discover the recipe for " + Label(piece) + " first."); } Vector3 position = part.Position; PrivateArea component = ((Component)piece).GetComponent<PrivateArea>(); if (!PrivateArea.CheckAccess(position, Object.op_Implicit((Object)(object)component) ? component.m_radius : 0f, true, (Object)(object)component != (Object)null)) { throw new InvalidOperationException("Part of the structure is inside a protected ward."); } if (Location.IsInsideNoBuildLocation(position)) { throw new InvalidOperationException("Part of the structure is inside a no-build area."); } if (!ZoneSystem.instance.IsZoneLoaded(position)) { throw new InvalidOperationException("Part of the structure is in unloaded terrain. Move closer before placing it."); } if (!piece.m_allowedInDungeons && Character.InInterior(position) && !EnvMan.instance.CheckInteriorBuildingOverride() && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)34)) { throw new InvalidOperationException(Label(piece) + " cannot be built in a dungeon."); } if ((int)piece.m_onlyInBiome != 0 && (Heightmap.FindBiome(position) & piece.m_onlyInBiome) == 0) { throw new InvalidOperationException("Wrong biome for " + Label(piece)); } if (piece.m_onlyInTeleportArea && !Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(position, (Type)16, 0f))) { throw new InvalidOperationException(Label(piece) + " needs a teleport area."); } Heightmap val = Heightmap.FindHeightmap(position); if (!piece.m_allowedInDeepSnow && Object.op_Implicit((Object)(object)val) && (int)Heightmap.FindBiome(position) == 64 && val.GetCultivationMask(position) > player.m_deepSnowBuildHeight) { throw new InvalidOperationException("Snow is too deep for " + Label(piece)); } if (piece.m_noInWater && position.y < ZoneSystem.instance.m_waterLevel) { throw new InvalidOperationException(Label(piece) + " cannot be built under water."); } if ((piece.m_groundOnly || piece.m_groundPiece) && (!ZoneSystem.instance.GetGroundHeight(position, ref num) || Mathf.Abs(position.y - num) > 0.75f)) { throw new InvalidOperationException(Label(piece) + " must touch the ground. Choose flatter terrain."); } if (!flag && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)27) && Object.op_Implicit((Object)(object)piece.m_craftingStation) && !HasStation(piece.m_craftingStation.m_name, position, 0f, plan)) { throw new InvalidOperationException("A " + Label(piece.m_craftingStation) + " must cover " + Label(piece) + "."); } StationExtension component2 = ((Component)piece).GetComponent<StationExtension>(); if (Object.op_Implicit((Object)(object)component2)) { if (!HasStation(component2.m_craftingStation.m_name, position, component2.m_maxStationDistance, plan)) { throw new InvalidOperationException("Station extension is too far from its crafting station."); } if (piece.m_spaceRequirement > 0f && Physics.OverlapSphere(position, piece.m_spaceRequirement, LayerMask.GetMask(new string[1] { "piece" }), (QueryTriggerInteraction)1).Any((Collider c) => Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent<StationExtension>()))) { throw new InvalidOperationException("A station extension needs more space."); } if (plan.Any((BuildPart p) => p != part && Object.op_Implicit((Object)(object)((Component)p.Piece).GetComponent<StationExtension>()) && Vector3.Distance(p.Position, position) < piece.m_spaceRequirement)) { throw new InvalidOperationException("Two station extensions are too close together."); } } ValidateCollisions(part, player); } } private static void ValidateCollisions(BuildPart part, Player player) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = ((Component)part.Preview).GetComponentsInChildren<Collider>(); int mask = LayerMask.GetMask(new string[7] { "piece", "piece_nonsolid", "Default", "static_solid", "character", "character_net", "character_ghost" }); Collider[] array = componentsInChildren; Vector3 val3 = default(Vector3); float num = default(float); foreach (Collider val in array) { if (!val.enabled || val.isTrigger) { continue; } Bounds bounds = val.bounds; Collider[] array2 = Physics.OverlapBox(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents + Vector3.one * 0.05f, Quaternion.identity, mask, (QueryTriggerInteraction)1); foreach (Collider val2 in array2) { if (!((Component)val2).transform.IsChildOf(player.m_placementGhost.transform)) { Piece componentInParent = ((Component)val2).GetComponentInParent<Piece>(); if (Object.op_Implicit((Object)(object)componentInParent) && Utils.GetPrefabName(((Component)componentInParent).gameObject) == ((Object)((Component)part.Piece).gameObject).name && Vector3.Distance(((Component)componentInParent).transform.position, part.Position) < 0.08f && Quaternion.Angle(((Component)componentInParent).transform.rotation, part.Rotation) < 1f) { throw new InvalidOperationException("An identical piece already occupies part of this structure."); } bool flag = Object.op_Implicit((Object)(object)((Component)val2).GetComponentInParent<Character>()); if ((flag || part.Piece.m_noClipping) && Physics.ComputePenetration(val, ((Component)val).transform.position, ((Component)val).transform.rotation, val2, ((Component)val2).transform.position, ((Component)val2).transform.rotation, ref val3, ref num) && num > (flag ? 0f : 0.2f)) { throw new InvalidOperationException(flag ? "A character is blocking part of the structure." : "Part of the structure clips an existing object."); } } } } } private static bool HasStation(string name, Vector3 position, float extensionRange, BuildPart[] plan) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)((extensionRange > 0f) ? CraftingStation.FindClosestStationInRange(name, position, extensionRange) : CraftingStation.HaveBuildStationInRange(name, position)))) { return true; } return plan.Any(delegate(BuildPart p) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) CraftingStation componentInChildren = ((Component)p.Piece).GetComponentInChildren<CraftingStation>(); return Object.op_Implicit((Object)(object)componentInChildren) && componentInChildren.m_name == name && Vector3.Distance(position, p.Position) < ((extensionRange > 0f) ? extensionRange : componentInChildren.m_rangeBuild); }); } private static string Label(Piece piece) { return Localization.instance.Localize(piece.m_name); } private static string Label(CraftingStation station) { return Localization.instance.Localize(station.m_name); } } internal sealed class StructureEntry { internal Blueprint Blueprint; internal GameObject Prefab; internal Piece[] Pieces; internal Requirement[] Costs; internal readonly List<Mesh> OwnedMeshes = new List<Mesh>(); } internal sealed class StructureCatalog : IDisposable { private readonly SaveStructurePlugin plugin; private readonly Dictionary<string, StructureEntry> entries = new Dictionary<string, StructureEntry>(StringComparer.Ordinal); private GameObject? container; private ZNetScene? scene; private PieceTable? hammerTable; private float retryAt; internal StructureCatalog(SaveStructurePlugin plugin) { this.plugin = plugin; } internal void EnsureRegistered() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown if (((Object)(object)scene == (Object)(object)ZNetScene.instance && Object.op_Implicit((Object)(object)hammerTable)) || Time.unscaledTime < retryAt || !Object.op_Implicit((Object)(object)ObjectDB.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } retryAt = Time.unscaledTime + 3f; Clear(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); if (!Object.op_Implicit((Object)(object)itemPrefab)) { return; } hammerTable = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces; if (!Object.op_Implicit((Object)(object)hammerTable)) { return; } scene = ZNetScene.instance; container = new GameObject("SaveStructure prefab storage"); container.SetActive(false); container.transform.SetParent(((Component)plugin).transform, false); foreach (Blueprint item in plugin.Store.Load()) { try { Register(item); } catch (Exception ex) { plugin.Warn("Could not load '" + item.Name + "': " + ex.Message); } } RefreshPlayer(); } internal Piece[] Resolve(Blueprint blueprint) { blueprint.Validate(plugin.MaxPieces.Value); return blueprint.Pieces.Select(delegate(SavedPiece saved) { GameObject prefab = ZNetScene.instance.GetPrefab(saved.Prefab); if (!Object.op_Implicit((Object)(object)prefab)) { throw new InvalidOperationException("Missing piece '" + saved.Prefab + "'. Install its original mod first."); } ValidatePrefab(prefab); return prefab.GetComponent<Piece>(); }).ToArray(); } internal void ValidatePrefab(GameObject prefab) { Piece component = prefab.GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>()) || !Object.op_Implicit((Object)(object)hammerTable) || !hammerTable.m_pieces.Contains(prefab)) { throw new InvalidOperationException("Only pieces available from the Hammer can be saved."); } if (component.m_repairPiece || component.m_removePiece || component.m_harvest || Object.op_Implicit((Object)(object)prefab.GetComponent<Ship>()) || Object.op_Implicit((Object)(object)prefab.GetComponent<Vagon>()) || Object.op_Implicit((Object)(object)prefab.GetComponent<ItemDrop>()) || Object.op_Implicit((Object)(object)prefab.GetComponentInChildren<TerrainModifier>(true)) || Object.op_Implicit((Object)(object)prefab.GetComponentInChildren<TerrainOp>(true)) || prefab.GetComponentsInChildren<ZNetView>(true).Length != 1) { throw new InvalidOperationException("Terrain tools, vehicles, item displays as items, and pieces with multiple network objects are not supported."); } if (component.m_waterPiece || component.m_cultivatedGroundOnly || component.m_vegetationGroundOnly || Object.op_Implicit((Object)(object)component.m_mustConnectTo) || component.m_blockingPieces.Count > 0 || component.m_requireDeepSnow) { throw new InvalidOperationException("This piece uses special terrain or connection rules and is not supported yet."); } } internal void Register(Blueprint blueprint) { //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) string text = "SaveStructure_" + blueprint.Id; if (entries.ContainsKey(text)) { return; } if (!Object.op_Implicit((Object)(object)container) || !Object.op_Implicit((Object)(object)hammerTable) || !Object.op_Implicit((Object)(object)scene)) { throw new InvalidOperationException("The Hammer menu is not ready yet."); } if (Object.op_Implicit((Object)(object)scene.GetPrefab(text))) { throw new InvalidOperationException("A prefab with the same ID already exists."); } Piece[] pieces = Resolve(blueprint); Dictionary<string, ItemDrop> resourceItems = new Dictionary<string, ItemDrop>(StringComparer.Ordinal); Dictionary<string, Piece> originals = (from p in blueprint.Pieces.Select((SavedPiece p, int i) => (Prefab: p.Prefab, Piece: pieces[i])) group p by p.Prefab).ToDictionary((IGrouping<string, (string Prefab, Piece Piece)> g) => g.Key, (IGrouping<string, (string Prefab, Piece Piece)> g) => g.First().Piece); Requirement[] array = ((IEnumerable<KeyValuePair<string, int>>)StructureMath.TotalCosts(blueprint.Pieces, (string prefab) => originals[prefab].m_resources.Select(delegate(Requirement r) { if (!Object.op_Implicit((Object)(object)r.m_resItem)) { throw new InvalidOperationException("A required material is missing for " + prefab); } string name = r.m_resItem.m_itemData.m_shared.m_name; resourceItems[name] = r.m_resItem; return new KeyValuePair<string, int>(name, r.m_amount); })).OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> x) => x.Key, StringComparer.Ordinal)).Select((Func<KeyValuePair<string, int>, Requirement>)((KeyValuePair<string, int> x) => new Requirement { m_resItem = resourceItems[x.Key], m_amount = x.Value, m_amountPerLevel = 0, m_recover = false })).ToArray(); StructureEntry structureEntry = new StructureEntry { Blueprint = blueprint, Pieces = pieces, Costs = array }; GameObject val = new GameObject(text); val.SetActive(false); val.transform.SetParent(container.transform, false); structureEntry.Prefab = val; try { for (int num = 0; num < pieces.Length; num++) { GameObject val2 = PreviewFactory.CopyGeometry(((Component)pieces[num]).gameObject, val.transform, structureEntry.OwnedMeshes); val2.transform.localPosition = blueprint.Pieces[num].Position.Unity(); val2.transform.localRotation = blueprint.Pieces[num].Rotation.Unity(); List<Transform> list = new List<Transform>(); pieces[num].GetSnapPoints(list); foreach (Transform item in list) { GameObject val3 = new GameObject("snap") { tag = "snappoint" }; val3.transform.SetParent(val.transform, false); val3.transform.localPosition = val2.transform.localPosition + val2.transform.localRotation * Vector3.Scale(((Component)pieces[num]).transform.InverseTransformPoint(item.position), val2.transform.localScale); } } Piece obj = val.AddComponent<Piece>(); obj.m_name = "Structure: " + blueprint.Name; obj.m_description = $"{blueprint.Pieces.Count} pieces. Builds the whole structure from your inventory.\nMaterials: " + string.Join(", ", array.Select((Requirement c) => $"{c.m_amount} {Localization.instance.Localize(c.m_resItem.m_itemData.m_shared.m_name)}")); obj.m_icon = pieces[0].m_icon; obj.m_category = (PieceCategory)0; obj.m_usage = (UsageTagFlags)4; obj.m_resources = array; obj.m_clipEverything = true; obj.m_allowedInDungeons = true; obj.m_allowedInDeepSnow = true; obj.m_canBeRemoved = false; val.SetActive(true); hammerTable.m_pieces.Add(val); scene.m_namedPrefabs.Add(StringExtensionMethods.GetStableHashCode(text), val); entries.Add(text, structureEntry); } catch { hammerTable.m_pieces.Remove(val); Object.Destroy((Object)(object)val); foreach (Mesh ownedMesh in structureEntry.OwnedMeshes) { Object.Destroy((Object)(object)ownedMesh); } throw; } } internal bool TryGet(Piece piece, out StructureEntry entry) { if (Object.op_Implicit((Object)(object)piece) && entries.TryGetValue(Utils.GetPrefabName(((Component)piece).gameObject), out entry)) { return true; } entry = null; return false; } internal void AddKnownNames(PieceTable table, HashSet<string> names) { if ((Object)(object)table != (Object)(object)hammerTable) { return; } foreach (StructureEntry value in entries.Values) { names.Add(value.Prefab.GetComponent<Piece>().m_name); } } internal void RefreshPlayer() { if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Player.m_localPlayer.UpdateAvailablePiecesList(); } } internal void Clear() { if (entries.Count == 0 && !Object.op_Implicit((Object)(object)container) && !Object.op_Implicit((Object)(object)hammerTable)) { return; } foreach (KeyValuePair<string, StructureEntry> entry in entries) { if (Object.op_Implicit((Object)(object)hammerTable)) { hammerTable.m_pieces.Remove(entry.Value.Prefab); } if (Object.op_Implicit((Object)(object)scene) && (Object)(object)scene.GetPrefab(entry.Key) == (Object)(object)entry.Value.Prefab) { scene.m_namedPrefabs.Remove(StringExtensionMethods.GetStableHashCode(entry.Key)); } foreach (Mesh ownedMesh in entry.Value.OwnedMeshes) { if (Object.op_Implicit((Object)(object)ownedMesh)) { Object.Destroy((Object)(object)ownedMesh); } } } entries.Clear(); if (Object.op_Implicit((Object)(object)container)) { Object.Destroy((Object)(object)container); } container = null; hammerTable = null; scene = null; } public void Dispose() { Clear(); } } internal static class PreviewFactory { internal static GameObject CopyGeometry(GameObject source, Transform inactiveParent, List<Mesh> ownedMeshes) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown GameObject val = new GameObject(((Object)source).name + "_preview"); val.SetActive(false); val.transform.SetParent(inactiveParent, false); CopyNode(source.transform, val.transform, ownedMeshes); val.SetActive(true); return val; } private static void CopyNode(Transform source, Transform target, List<Mesh> ownedMeshes) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown target.localPosition = source.localPosition; target.localRotation = source.localRotation; target.localScale = source.localScale; ((Component)target).gameObject.layer = ((Component)source).gameObject.layer; MeshFilter component = ((Component)source).GetComponent<MeshFilter>(); MeshRenderer component2 = ((Component)source).GetComponent<MeshRenderer>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh) && Object.op_Implicit((Object)(object)component2) && ((Renderer)component2).enabled) { ((Component)target).gameObject.AddComponent<MeshFilter>().sharedMesh = component.sharedMesh; ((Renderer)((Component)target).gameObject.AddComponent<MeshRenderer>()).sharedMaterials = ((Renderer)component2).sharedMaterials; } SkinnedMeshRenderer component3 = ((Component)source).GetComponent<SkinnedMeshRenderer>(); if (Object.op_Implicit((Object)(object)component3) && Object.op_Implicit((Object)(object)component3.sharedMesh) && ((Renderer)component3).enabled) { Mesh val = new Mesh(); component3.BakeMesh(val); ownedMeshes.Add(val); ((Component)target).gameObject.AddComponent<MeshFilter>().sharedMesh = val; ((Renderer)((Component)target).gameObject.AddComponent<MeshRenderer>()).sharedMaterials = ((Renderer)component3).sharedMaterials; } Collider[] components = ((Component)source).GetComponents<Collider>(); foreach (Collider val2 in components) { if (!val2.enabled || val2.isTrigger) { continue; } BoxCollider val3 = (BoxCollider)(object)((val2 is BoxCollider) ? val2 : null); if (val3 == null) { SphereCollider val4 = (SphereCollider)(object)((val2 is SphereCollider) ? val2 : null); if (val4 == null) { CapsuleCollider val5 = (CapsuleCollider)(object)((val2 is CapsuleCollider) ? val2 : null); if (val5 == null) { MeshCollider val6 = (MeshCollider)(object)((val2 is MeshCollider) ? val2 : null); if (val6 != null) { MeshCollider obj = ((Component)target).gameObject.AddComponent<MeshCollider>(); obj.sharedMesh = val6.sharedMesh; obj.convex = val6.convex; } } else { CapsuleCollider obj2 = ((Component)target).gameObject.AddComponent<CapsuleCollider>(); obj2.center = val5.center; obj2.radius = val5.radius; obj2.height = val5.height; obj2.direction = val5.direction; } } else { SphereCollider obj3 = ((Component)target).gameObject.AddComponent<SphereCollider>(); obj3.center = val4.center; obj3.radius = val4.radius; } } else { BoxCollider obj4 = ((Component)target).gameObject.AddComponent<BoxCollider>(); obj4.center = val3.center; obj4.size = val3.size; } } foreach (Transform item in source) { Transform val7 = item; if (((Component)val7).gameObject.activeSelf && !(((Object)val7).name == "_GhostOnly")) { GameObject val8 = new GameObject(((Object)val7).name); val8.transform.SetParent(target, false); CopyNode(val7, val8.transform, ownedMeshes); } } } } } namespace SaveStructure.Core { public sealed class Blueprint { [JsonProperty(/*Could not decode attribute arguments.*/)] public int SchemaVersion { get; set; } = 1; [JsonProperty(/*Could not decode attribute arguments.*/)] public string Id { get; set; } = Guid.NewGuid().ToString("N"); [JsonProperty(/*Could not decode attribute arguments.*/)] public string Name { get; set; } = ""; public string GameVersion { get; set; } = ""; public DateTime SavedUtc { get; set; } = DateTime.UtcNow; [JsonProperty(/*Could not decode attribute arguments.*/)] public List<SavedPiece> Pieces { get; set; } = new List<SavedPiece>(); public void Validate(int maxPieces = 2000) { if (SchemaVersion != 1) { throw new FormatException("Unsupported structure file version."); } if (!Guid.TryParseExact(Id, "N", out var _)) { throw new FormatException("Invalid structure ID."); } Name = ValidateName(Name); if (Pieces == null || Pieces.Count == 0 || Pieces.Count > maxPieces) { throw new FormatException($"A structure must contain 1–{maxPieces} pieces."); } foreach (SavedPiece piece in Pieces) { if (piece == null || string.IsNullOrWhiteSpace(piece.Prefab) || piece.Prefab.Length > 200) { throw new FormatException("Invalid piece prefab name."); } if (!piece.Position.IsFinite || piece.Position.ToVector().Length() > 128f) { throw new FormatException("Structure pieces must be within 128 metres of the anchor."); } float num = piece.Rotation.ToQuaternion().LengthSquared(); if (!piece.Rotation.IsFinite || Math.Abs(num - 1f) > 0.01f) { throw new FormatException("Invalid piece rotation."); } } } public static string ValidateName(string name) { name = name?.Trim() ?? ""; if (name.Length == 0 || name.Length > 60 || name.Any(char.IsControl) || name.IndexOfAny(new char[3] { '<', '>', '$' }) >= 0) { throw new FormatException("Use a name of 1–60 characters, without control characters, <, >, or $."); } return name; } } public sealed class SavedPiece { [JsonProperty(/*Could not decode attribute arguments.*/)] public string Prefab { get; set; } = ""; [JsonProperty(/*Could not decode attribute arguments.*/)] public PositionData Position { get; set; } [JsonProperty(/*Could not decode attribute arguments.*/)] public RotationData Rotation { get; set; } = RotationData.From(Quaternion.Identity); } public struct PositionData { public float X { get; set; } public float Y { get; set; } public float Z { get; set; } [JsonIgnore] public bool IsFinite { get { if (Numbers.Finite(X) && Numbers.Finite(Y)) { return Numbers.Finite(Z); } return false; } } public Vector3 ToVector() { return new Vector3(X, Y, Z); } public static PositionData From(Vector3 v) { return new PositionData { X = v.X, Y = v.Y, Z = v.Z }; } } public struct RotationData { public float X { get; set; } public float Y { get; set; } public float Z { get; set; } public float W { get; set; } [JsonIgnore] public bool IsFinite { get { if (Numbers.Finite(X) && Numbers.Finite(Y) && Numbers.Finite(Z)) { return Numbers.Finite(W); } return false; } } public Quaternion ToQuaternion() { return new Quaternion(X, Y, Z, W); } public static RotationData From(Quaternion q) { return new RotationData { X = q.X, Y = q.Y, Z = q.Z, W = q.W }; } } internal static class Numbers { public static bool Finite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } } public static class StructureMath { public static SavedPiece Capture(string prefab, Vector3 worldPosition, Quaternion worldRotation, Vector3 anchor, Quaternion heading) { Quaternion quaternion = Quaternion.Inverse(heading); return new SavedPiece { Prefab = prefab, Position = PositionData.From(Vector3.Transform(worldPosition - anchor, quaternion)), Rotation = RotationData.From(Quaternion.Normalize(quaternion * worldRotation)) }; } public static (Vector3 Position, Quaternion Rotation) Place(SavedPiece piece, Vector3 anchor, Quaternion heading) { return (Position: anchor + Vector3.Transform(piece.Position.ToVector(), heading), Rotation: Quaternion.Normalize(heading * piece.Rotation.ToQuaternion())); } public static Dictionary<string, int> TotalCosts(IEnumerable<SavedPiece> pieces, Func<string, IEnumerable<KeyValuePair<string, int>>> getCosts) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (SavedPiece piece in pieces) { foreach (KeyValuePair<string, int> item in getCosts(piece.Prefab)) { if (item.Value < 0 || string.IsNullOrWhiteSpace(item.Key)) { throw new FormatException("Invalid material cost."); } if (item.Value != 0) { dictionary.TryGetValue(item.Key, out var value); dictionary[item.Key] = checked(value + item.Value); } } } return dictionary; } } public sealed class BlueprintStore { private const long MaxFileSize = 8388608L; private readonly string directory; private readonly Action<string> warn; private readonly JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = (TypeNameHandling)0, MaxDepth = 16, MissingMemberHandling = (MissingMemberHandling)0 }; public BlueprintStore(string directory, Action<string> warn) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown this.directory = directory; this.warn = warn; Directory.CreateDirectory(directory); } public List<Blueprint> Load() { List<Blueprint> list = new List<Blueprint>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string item in Directory.EnumerateFiles(directory, "*.json").OrderBy<string, string>((string x) => x, StringComparer.Ordinal)) { try { if (new FileInfo(item).Length > 8388608) { throw new FormatException("File exceeds 8 MB."); } Blueprint blueprint = JsonConvert.DeserializeObject<Blueprint>(File.ReadAllText(item), settings) ?? throw new FormatException("Empty structure file."); blueprint.Validate(); if (hashSet.Contains(blueprint.Id) || hashSet2.Contains(blueprint.Name)) { throw new FormatException("Duplicate structure ID or name."); } hashSet.Add(blueprint.Id); hashSet2.Add(blueprint.Name); list.Add(blueprint); } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is JsonException || ex is FormatException) { warn("Skipped " + Path.GetFileName(item) + ": " + ex.Message); } } return list; } public void SaveNew(Blueprint blueprint) { blueprint.Validate(); if (Load().Any((Blueprint x) => string.Equals(x.Name, blueprint.Name, StringComparison.OrdinalIgnoreCase))) { throw new InvalidOperationException("A structure with that name already exists. Choose another name."); } string destFileName = Path.Combine(directory, blueprint.Id + ".json"); string text = Path.Combine(directory, blueprint.Id + "." + Guid.NewGuid().ToString("N") + ".tmp"); try { byte[] bytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(JsonConvert.SerializeObject((object)blueprint, (Formatting)1, settings)); if ((long)bytes.Length > 8388608L) { throw new FormatException("Structure file exceeds 8 MB."); } using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(flushToDisk: true); } File.Move(text, destFileName); } finally { if (File.Exists(text)) { File.Delete(text); } } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }