Иконка ресурса

FREE CopyPaste : 4.3.0 4.3.0

Нет прав для скачивания
C#:
//If debug is defined it will add a stopwatch to the paste and copydata which can be used to profile copying and pasting.
// #define DEBUG
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Libraries.Covalence;
using ProtoBuf;
using UnityEngine;
using Graphics = System.Drawing.Graphics;
using WrapMode = System.Drawing.Drawing2D.WrapMode;
#if DEBUG
using System.Diagnostics;
#endif
// ReSharper disable SpecifyACultureInStringConversionExplicitly
/*
 * CREDITS
 *
 * Orange - Saving ContainerIOEntity
 * UIP88 - Turrets fix
 * bsdinis - Wire fix
 * nivex - Ownership option, sign fix
 * bmgjet - Wallpapers, pattern firework, industrial
 * DezLife - CCTV fix
 * Wulf - Skipping 4.1.24 :D
 * MediocratyItself - added sizing/scaling saving feature
 * 
 */
namespace Oxide.Plugins {
    [Info("Copy Paste", "misticos", "4.3.0")]
    [Description("Copy and paste buildings to save them or move them")]
    public class CopyPaste : CovalencePlugin {
        // ReSharper disable once Unity.IncorrectMonoBehaviourInstantiation
        private readonly Item _emptyItem = new Item {
            info = new ItemDefinition()
        }
        ;
        private readonly IPlayer _consolePlayer = new RustConsolePlayer();
        private int _copyLayer = LayerMask.GetMask("Construction", "Prevent Building", "Construction Trigger", "Trigger", "Deployed", "Default", "Ragdoll", "Vehicle Large"), _groundLayer = LayerMask.GetMask("Terrain", "Default"), _rayCopy = LayerMask.GetMask("Construction", "Deployed", "Tree", "Resource", "Prevent Building", "Vehicle Large"), _rayPaste = LayerMask.GetMask("Construction", "Deployed", "Tree", "Terrain", "World", "Water", "Prevent Building");
        private string _copyPermission = "copypaste.copy", _listPermission = "copypaste.list", _pastePermission = "copypaste.paste", _pastebackPermission = "copypaste.pasteback", _undoPermission = "copypaste.undo", _subDirectory = "copypaste/";
        private readonly HashSet<string> _dlcPrefabs = new();
        private readonly HashSet<int> _dlcItemIds = new();
        private readonly HashSet<ulong> _paidSkinIds = new();
        private readonly Dictionary<string, ItemDefinition> _prefabToItemDef = new();
        private readonly Dictionary<ItemDefinition, string> _itemDefToPrefab = new();
        private readonly uint _floorFramePrefabId = StringPool.Get("assets/prefabs/building core/floor.frame/floor.frame.prefab");
        private readonly uint _floorTriangleFramePrefabId = StringPool.Get("assets/prefabs/building core/floor.triangle.frame/floor.triangle.frame.prefab");
        private const float LegacyElevatorLiftMaxHorizontalDistanceSqr = 1f;
        private const float LegacyElevatorLiftVerticalTolerance = 0.5f;
        private const float LegacyElevatorShaftMatchMaxHorizontalDistanceSqr = 1f;
        private const float LegacyElevatorShaftMatchForwardDotMin = 0.98f;
        private bool _pasteReady;
        private readonly List<PasteData> _pendingPastes = new();
        public class LastPaste {
            public string Filename;
            public List<BaseEntity> Entities = new();
        }
        private Dictionary<string, Stack<LastPaste>> _lastPastes = new();
        private Dictionary<string, SignSize> _signSizes = new Dictionary<string, SignSize> {
            {
                "photoframe.landscape", new SignSize(320, 240)
            }
            , {
                "photoframe.large", new SignSize(320, 240)
            }
            , {
                "photoframe.portrait", new SignSize(320, 240)
            }
            , {
                "sign.pictureframe.landscape", new SignSize(256, 192)
            }
            , {
                "sign.pictureframe.tall", new SignSize(128, 512)
            }
            , {
                "sign.pictureframe.portrait", new SignSize(205, 256)
            }
            , {
                "sign.pictureframe.xxl", new SignSize(1024, 512)
            }
            , {
                "sign.pictureframe.xl", new SignSize(512, 512)
            }
            , {
                "sign.small.wood", new SignSize(256, 128)
            }
            , {
                "sign.medium.wood", new SignSize(512, 256)
            }
            , {
                "sign.large.wood", new SignSize(512, 256)
            }
            , {
                "sign.huge.wood", new SignSize(1024, 256)
            }
            , {
                "sign.hanging.banner.large", new SignSize(256, 1024)
            }
            , {
                "sign.pole.banner.large", new SignSize(256, 1024)
            }
            , {
                "sign.post.single", new SignSize(256, 128)
            }
            , {
                "sign.post.double", new SignSize(512, 512)
            }
            , {
                "sign.post.town", new SignSize(512, 256)
            }
            , {
                "sign.post.town.roof", new SignSize(512, 256)
            }
            , {
                "sign.hanging", new SignSize(256, 512)
            }
            , {
                "sign.hanging.ornate", new SignSize(512, 256)
            }
            , {
                "sign.neon.xl.animated", new SignSize(256, 256)
            }
            , {
                "sign.neon.xl", new SignSize(256, 256)
            }
            , {
                "sign.neon.125x215.animated", new SignSize(256, 128)
            }
            , {
                "sign.neon.125x215", new SignSize(256, 128)
            }
            , {
                "sign.neon.125x125", new SignSize(128, 128)
            }
            ,
        }
        ;
        private HashSet<Type> ItemModAssociatedEntities = new() {
            typeof(PaintedItemStorageEntity), typeof(PhotoEntity), typeof(SignContent), typeof(HeadEntity), typeof(PagerEntity), typeof(MobileInventoryEntity), typeof(Cassette)
        }
        ;
        private List<BaseEntity.Slot> _checkSlots = new List<BaseEntity.Slot> {
            BaseEntity.Slot.Lock, BaseEntity.Slot.UpperModifier, BaseEntity.Slot.MiddleModifier, BaseEntity.Slot.LowerModifier
        }
        ;
        public enum CopyMechanics {
            Building, Proximity
        }
        private class SignSize {
            public int Width;
            public int Height;
            public SignSize(int width, int height) {
                Width = width;
                Height = height;
            }
        }
        public enum SkinsMode {
            NoSkins = 0, AllSkins = 1, NoPaidSkins = 2, AllowSpecifiedOnly = 3, BlockSpecifiedOnly = 4
        }
        public enum LoopMode {
            TimedDelay = 1, PerFrame = 2, Instant = 3
        }
        public enum PasteErrorMode {
            Stop = 1, UndoPasted = 2, Continue = 3
        }
        //Config
        private ConfigData _config;
        private class ConfigData {
            [JsonProperty(PropertyName = "Copy Options", Order = 7)]
            public CopyOptions Copy {
                get;
                set;
            }
            [JsonProperty(PropertyName = "Paste Options", Order = 8)]
            public PasteOptions Paste {
                get;
                set;
            }
            [JsonProperty(PropertyName = "Amount of entities to paste per batch. Use to tweak performance impact of pasting", Order = 1)]
            [DefaultValue(5)]
            public int PasteBatchSize = 5;
            [JsonProperty(PropertyName = "Amount of entities to copy per batch. Use to tweak performance impact of copying", Order = 2)]
            [DefaultValue(100)]
            public int CopyBatchSize = 100;
            [JsonProperty(PropertyName = "Amount of entities to undo per batch. Use to tweak performance impact of undoing", Order = 3)]
            [DefaultValue(5)]
            public int UndoBatchSize = 5;
            [JsonProperty(PropertyName = "Loop Execution", Order = 4)]
            public LoopExecutionConfig LoopExecution {
                get;
                set;
            }
            = new();
            [JsonProperty(PropertyName = "Prevent These Prefabs From Spawning", ObjectCreationHandling = ObjectCreationHandling.Replace, Order = 5), DefaultValue(typeof(List<string>), "")]
            public List<string> BlockedPrefabs = new();
            [JsonProperty(PropertyName = "Enable data saving feature", Order = 6)]
            [DefaultValue(true)]
            public bool DataSaving = true;
            public class LoopExecutionConfig {
                [JsonProperty(PropertyName = "Mode: (1=timed delay [safest], 2=per-frame [balanced], 3=instant [lag])", Order = 1)]
                [DefaultValue((int)LoopMode.TimedDelay)]
                public int Mode {
                    get;
                    set;
                }
                = (int)LoopMode.TimedDelay;
                [JsonProperty(PropertyName = "Timed delay between paste batches, in seconds", Order = 2)]
                [DefaultValue(0.075)]
                public float PasteBatchDelay = 0.075f;
                [JsonProperty(PropertyName = "Timed delay between copy batches, in seconds", Order = 3)]
                [DefaultValue(0.075)]
                public float CopyBatchDelay = 0.075f;
                [JsonProperty(PropertyName = "Timed delay between undo batches, in seconds", Order = 4)]
                [DefaultValue(0.075)]
                public float UndoBatchDelay = 0.075f;
            }
            public class CopyOptions {
                [JsonProperty(PropertyName = "Check radius from each entity (true/false)")]
                [DefaultValue(true)]
                public bool EachToEach {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "Share (true/false)")]
                [DefaultValue(false)]
                public bool Share {
                    get;
                    set;
                }
                = false;
                [JsonProperty(PropertyName = "Tree (true/false)")]
                [DefaultValue(false)]
                public bool Tree {
                    get;
                    set;
                }
                = false;
                [JsonProperty(PropertyName = "Default radius to look for entities from block")]
                [DefaultValue(3.0f)]
                public float Radius {
                    get;
                    set;
                }
                = 3.0f;
            }
            public class PasteOptions {
                [JsonProperty(PropertyName = "Auth (true/false)")]
                [DefaultValue(false)]
                public bool Auth {
                    get;
                    set;
                }
                = false;
                [JsonProperty(PropertyName = "Deployables (true/false)")]
                [DefaultValue(true)]
                public bool Deployables {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "Inventories (true/false)")]
                [DefaultValue(true)]
                public bool Inventories {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "Vending Machines (true/false)")]
                [DefaultValue(true)]
                public bool VendingMachines {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "Stability (true/false)")]
                [DefaultValue(true)]
                public bool Stability {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "EntityOwner (true/false)")]
                [DefaultValue(true)]
                public bool EntityOwner {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "DLC items and deployables (true/false)")]
                [DefaultValue(true)]
                public bool Dlc {
                    get;
                    set;
                }
                = true;
                [JsonProperty(PropertyName = "Skins (0=no skins, 1=all, 2=no paid skins, 3=allow specified only, 4=block specified only)")]
                [DefaultValue((int)CopyPaste.SkinsMode.AllSkins)]
                public int SkinsMode {
                    get;
                    set;
                }
                = (int)CopyPaste.SkinsMode.AllSkins;
                [JsonProperty(PropertyName = "When an entity fails to paste: (1=stop, 2=undo pasted, 3=continue)")]
                [DefaultValue((int)CopyPaste.PasteErrorMode.Stop)]
                public int PasteErrorMode {
                    get;
                    set;
                }
                = (int)CopyPaste.PasteErrorMode.Stop;
                [JsonProperty(PropertyName = "Specified Skins (skin id, like 2601577757, or item shortname for redirected skins, like hazmatsuit.spacesuit)")]
                public List<object> SpecifiedSkins {
                    get;
                    set;
                }
                = new();
                [JsonIgnore]
                public List<ulong> SpecifiedSkinIds = new();
                [JsonIgnore]
                public List<string> SpecifiedSkinRedirects = new();
            }
        }
        private void LoadVariables() {
            Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
            _config = Config.ReadObject<ConfigData>();
            _config.Copy ??= new();
            _config.Paste ??= new();
            _config.LoopExecution ??= new();
            _config.BlockedPrefabs ??= new();
            _config.Paste.SpecifiedSkins ??= new();
            if (!IsValidSkinsMode(_config.Paste.SkinsMode)) {
                PrintWarning("Invalid config value specified for 'Skins', resetting to default of 1 (all skins)");
                _config.Paste.SkinsMode = (int)SkinsMode.AllSkins;
            }
            if (!IsValidLoopMode(_config.LoopExecution.Mode)) {
                PrintWarning("Invalid config value specified for 'Loop Execution Mode', resetting to default of 1 (timed delay)");
                _config.LoopExecution.Mode = (int)LoopMode.TimedDelay;
            }
            if (_config.LoopExecution.Mode == (int)LoopMode.Instant) PrintWarning("Loop execution mode is set to instant. Large operations can lag the server and may kick players.");
            if (!IsValidPasteErrorMode(_config.Paste.PasteErrorMode)) {
                PrintWarning("Invalid config value specified for 'When an entity fails to paste', resetting to default of 1 (stop)");
                _config.Paste.PasteErrorMode = (int)PasteErrorMode.Stop;
            }
            for (var i = 0; i < _config.Paste.SpecifiedSkins.Count; i++) {
                var str = _config.Paste.SpecifiedSkins[i].ToString();
                if (string.IsNullOrEmpty(str)) continue;
                if (UInt64.TryParse(str, out var skinid)) _config.Paste.SpecifiedSkinIds.Add(skinid);
                else if (ItemManager.FindItemDefinition(str) != null) _config.Paste.SpecifiedSkinRedirects.Add(str);
                else PrintWarning($"Ignoring invalid item shortname in 'Specified Skins': {str}");
            }
            Config.WriteObject(_config, true);
        }
        protected override void LoadDefaultConfig() {
            var configData = new ConfigData {
                Copy = new ConfigData.CopyOptions(), Paste = new ConfigData.PasteOptions(), LoopExecution = new ConfigData.LoopExecutionConfig()
            }
            ;
            Config.WriteObject(configData, true);
        }
        private bool IsValidSkinsMode(int mode) => Enum.IsDefined(typeof(SkinsMode), mode);
        private bool IsValidLoopMode(int opt) => Enum.IsDefined(typeof(LoopMode), opt);
        private bool IsValidPasteErrorMode(int mode) => Enum.IsDefined(typeof(PasteErrorMode), mode);
        //Hooks
        private void Init() {
            permission.RegisterPermission(_copyPermission, this);
            permission.RegisterPermission(_listPermission, this);
            permission.RegisterPermission(_pastePermission, this);
            permission.RegisterPermission(_pastebackPermission, this);
            permission.RegisterPermission(_undoPermission, this);
            var compiledLangs = new Dictionary<string, Dictionary<string, string>>();
            foreach (var line in _messages) {
                foreach (var translate in line.Value) {
                    if (!compiledLangs.ContainsKey(translate.Key)) compiledLangs[translate.Key] = new Dictionary<string, string>();
                    compiledLangs[translate.Key][line.Key] = translate.Value;
                }
            }
            foreach (var cLangs in compiledLangs) {
                lang.RegisterMessages(cLangs.Value, this, cLangs.Key);
            }
            LoadVariables();
            Vis.colBuffer = new Collider[8192 * 16];
            JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
                Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            }
            ;
            ProcessItemDefinitions();
        }
        // Heavily influenced by k1lly0u's Player DLC API plugin
        private void ProcessItemDefinitions() {
            if ((Steamworks.SteamInventory.Definitions?.Length ?? 0) == 0) {
                timer.In(3f, ProcessItemDefinitions);
                return;
            }
            const string WORKSHOP_ID = "workshopid";
            foreach (Steamworks.InventoryDef inventoryDef in Steamworks.SteamInventory.Definitions) {
                if (ulong.TryParse(inventoryDef.GetProperty(WORKSHOP_ID), out ulong skinId)) _paidSkinIds.Add(skinId);
            }
            for (int i = 0; i < ItemSkinDirectory.Instance.skins.Length; i++) {
                ItemSkinDirectory.Skin skin = ItemSkinDirectory.Instance.skins[i];
                _paidSkinIds.Add((ulong)skin.id);
            }
            foreach (ItemDefinition itemDef in ItemManager.itemList) {
                string prefabPath = GetPrefabPathFromItemDef(itemDef);
                if (!string.IsNullOrEmpty(prefabPath)) {
                    _prefabToItemDef[prefabPath] = itemDef;
                    _itemDefToPrefab[itemDef] = prefabPath;
                }
                if (IsDlcItem(itemDef)) {
                    _dlcItemIds.Add(itemDef.itemid);
                    if (!string.IsNullOrEmpty(prefabPath)) _dlcPrefabs.Add(prefabPath);
                }
            }
            Puts($"Skin detection initialized: {_paidSkinIds.Count} official skins, {_dlcItemIds.Count} DLC items. Processing {_pendingPastes.Count} queued paste(s).");
            _pasteReady = true;
            for (int i = 0; i < _pendingPastes.Count; i++) {
                var pasteData = _pendingPastes[i];
                timer.Once(i * 0.1f, () => {
                    pasteData.StartLoop(PasteLoop(pasteData));
                }
                );
            }
            _pendingPastes.Clear();
        }
        private string GetPrefabPathFromItemDef(ItemDefinition def) {
            if (def.TryGetComponent<ItemModDeployable>(out var deployable)) return deployable.entityPrefab.resourcePath;
            if (def.TryGetComponent<ItemModEntity>(out var entity)) return entity.entityPrefab.resourcePath;
            if (def.TryGetComponent<ItemModEntityReference>(out var entityRef)) return entityRef.entityPrefab.resourcePath;
            return null;
        }
        public static bool IsDlcItem(ItemDefinition definition) {
            var parent = definition.Parent;
            return (definition.steamItem != null && definition.steamItem.id != 0) || (definition.steamDlc != null && definition.steamDlc.dlcAppID != 0) || (definition.Blueprint != null && definition.steamDlc != null) || (parent != null && parent.Blueprint != null && parent.steamDlc != null) || definition.isRedirectOf != null;
        }
        public ulong FilterSkinId(PasteData pasteData, ulong skinId) {
            if (skinId == 0) return skinId;
            return pasteData.SkinsMode switch {
                SkinsMode.NoSkins => 0, SkinsMode.NoPaidSkins when _paidSkinIds.Contains(skinId) => 0, SkinsMode.AllowSpecifiedOnly when !_config.Paste.SpecifiedSkinIds.Contains(skinId) => 0, SkinsMode.BlockSpecifiedOnly when _config.Paste.SpecifiedSkinIds.Contains(skinId) => 0, _ => skinId
            }
            ;
        }
        public bool GetItemDefinitionForPrefab(string prefabPath, out ItemDefinition def, bool useRedirect = true) {
            def = null;
            if (!_prefabToItemDef.TryGetValue(prefabPath, out def)) return false;
            if (useRedirect && def?.isRedirectOf != null) def = def.isRedirectOf;
            return def != null;
        }
        private bool ShouldRedirectForSkinsMode(PasteData pasteData, ItemDefinition itemDef) {
            var useRedirect = pasteData.SkinsMode == SkinsMode.NoSkins || pasteData.SkinsMode == SkinsMode.NoPaidSkins;
            if (pasteData.SkinsMode == SkinsMode.AllowSpecifiedOnly || pasteData.SkinsMode == SkinsMode.BlockSpecifiedOnly) {
                bool isInSpecifiedList = _config.Paste.SpecifiedSkinRedirects.Contains(itemDef.shortname);
                if (pasteData.SkinsMode == SkinsMode.AllowSpecifiedOnly && !isInSpecifiedList) useRedirect = true;
                else if (pasteData.SkinsMode == SkinsMode.BlockSpecifiedOnly && isInSpecifiedList) useRedirect = true;
            }
            return useRedirect;
        }
#region API
        private bool IsPasteReady() => _pasteReady;
        private object TryCopyFromSteamId(ulong userId, string filename, string[] args, Action callback = null) {
            var player = players.FindPlayerById(userId.ToString())?.Object as BasePlayer;
            if (player == null) return Lang("NOT_FOUND_PLAYER");
            RaycastHit hit;
            if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayCopy)) return Lang("NO_ENTITY_RAY", player.UserIDString);
            var entity = hit.GetEntity();
            if (!entity.IsValid()) return Lang("NO_ENTITY_RAY", player.UserIDString);
            return TryCopy(hit.point, entity.GetNetworkRotation().eulerAngles, filename, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player.IPlayer, callback);
        }
        private object TryPasteFromSteamId(ulong userId, string filename, string[] args, Action callback = null, Action<BaseEntity> callbackSpawned = null) {
            var player = players.FindPlayerById(userId.ToString())?.Object as BasePlayer;
            if (player == null) return Lang("NOT_FOUND_PLAYER");
            RaycastHit hit;
            if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayPaste)) return Lang("NO_ENTITY_RAY", player.UserIDString);
            return TryPaste(hit.point, filename, player.IPlayer, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, callback : callback, callbackSpawned : callbackSpawned).Item1;
        }
        private object TryPasteFromVector3(Vector3 pos, float rotationCorrection, string filename, string[] args, Action callback = null, Action<BaseEntity> callbackSpawned = null, int loopMode = 0) {
            return TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback : callback, callbackSpawned : callbackSpawned, loopMode : loopMode).Item1;
        }
        private ValueTuple<object, Action> TryPasteFromVector3Cancellable(Vector3 pos, float rotationCorrection, string filename, string[] args, Action callback = null, Action<BaseEntity> callbackSpawned = null, int loopMode = 0) {
            var result = TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback : callback, callbackSpawned : callbackSpawned, loopMode : loopMode);
            var pasteData = result.Item2;
            return new ValueTuple<object, Action>(result.Item1, () => pasteData.Cancelled = true);
        }
#endregion
        //Other methods
        private object CheckCollision(HashSet<Dictionary<string, object>> entities, Vector3 startPos, float radius) {
            foreach (var entityobj in entities) {
                if (Physics.CheckSphere((Vector3)entityobj["position"], radius, _copyLayer)) return Lang("BLOCKING_PASTE");
            }
            return true;
        }
        private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot) {
            const float maxDiff = 0.01f;
            var ents = Pool.Get<List<BaseEntity>>();
            try {
                Vis.Entities(pos, maxDiff, ents);
                foreach (var ent in ents) {
                    if (ent.IsDestroyed || ent.PrefabName != prefabname) continue;
                    if (Vector3.Distance(ent.transform.position, pos) > maxDiff) {
                        continue;
                    }
                    if (Vector3.Distance(ent.transform.rotation.eulerAngles, rot.eulerAngles) > maxDiff) {
                        continue;
                    }
                    return true;
                }
                return false;
            }
            finally {
                Pool.FreeUnmanaged(ref ents);
            }
        }
        private void RemoveEntity(BaseEntity entity) {
            if (!entity.IsValid() || entity.IsDestroyed) return;
            // Cleanup the hotspot beloning to the node.
            var ore = entity as OreResourceEntity;
            if (ore != null) {
                ore.CleanupBonus();
            }
            var io = entity as IOEntity;
            if (io != null) {
                try {
                    io.ClearConnections();
                }
                catch {
                }
                // this can throw
            }
            var autoTurret = entity as AutoTurret;
            if (autoTurret != null) {
                AutoTurret.interferenceUpdateList.Remove(autoTurret);
            }
            entity.Kill();
        }
        private IEnumerator UndoLoop(UndoData undoData) {
            var entities = undoData.EntitiesToUndo;
            var player = undoData.Player;
            for (var i = entities.Count - 1; i >= 0; i--) {
                var baseEntity = entities.ElementAt(i);
                if (baseEntity is IItemContainerEntity) {
                    entities.Remove(baseEntity);
                    // Don't .Kill() the drone storage as it'll cause Drone.Update_Server() to NRE
                    if (baseEntity is DroneStorage droneStorage && droneStorage.inventory != null) {
                        droneStorage.inventory.Clear();
                        continue;
                    }
                    RemoveEntity(baseEntity);
                }
            }
            int entityIndex = 0;
            // Take an amount of entities from the entity list (defined in config) and kill them. Will be repeated for every tick until there are no entities left.
            foreach (var p in entities) {
                RemoveEntity(p);
                if (++entityIndex % _config.UndoBatchSize == 0 && undoData.TryGetBatchYield(_config.LoopExecution.UndoBatchDelay, out var batchYield)) yield return batchYield;
            }
            if (player != null) {
                player.Reply(Lang("UNDO_SUCCESS", player.Id) + ": " + undoData.Filename);
                if (_lastPastes.TryGetValue(player.Id, out var checkFrom) && checkFrom.Count == 0) _lastPastes.Remove(player.Id);
            }
        }
        private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection, CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, IPlayer player, Action callback) {
            var currentLayer = _copyLayer;
            if (saveTree) currentLayer |= LayerMask.GetMask("Tree");
            var copyData = new CopyData {
                Filename = filename, CurrentLayer = currentLayer, RotCor = rotationCorrection, Range = range, SaveShare = saveShare, SaveTree = saveTree, CopyMechanics = copyMechanics, EachToEach = eachToEach, SourcePos = sourcePos, SourceRot = sourceRot, Player = player, BasePlayer = player.Object as BasePlayer, Callback = callback, ExecutionMode = (LoopMode)_config.LoopExecution.Mode
            }
            ;
            copyData.CheckFrom.Push(sourcePos);
            copyData.StartLoop(CopyLoop(copyData));
        }
        // Main loop for copy, will fetch all the data needed. Is called every tick untill copy is done (can't find any entities)
        private IEnumerator CopyLoop(CopyData copyData) {
            var checkFrom = copyData.CheckFrom;
            var houseList = copyData.HouseList;
            var buildingId = copyData.BuildingId;
            var copyMechanics = copyData.CopyMechanics;
            var range = copyData.Range;
            while (checkFrom.Count > 0) {
                var batchSize = checkFrom.Count < _config.CopyBatchSize ? checkFrom.Count : _config.CopyBatchSize;
                for (var i = 0; i < batchSize; i++) {
                    if (checkFrom.Count == 0) break;
                    var list = Pool.Get<List<BaseEntity>>();
                    try {
                        Vis.Entities(checkFrom.Pop(), range, list, copyData.CurrentLayer);
                        foreach (var entity in list) {
                            // Skip entities that are already in the list
                            if (!entity.IsValid() || entity.HasParent()) continue;
                            // Skip metal detector flags
                            if (entity.GetComponent<MetalDetectorSource>() != null) continue;
                            if (!houseList.Add(entity)) continue;
                            var buildingBlock = entity as BuildingBlock;
                            if (copyMechanics == CopyMechanics.Building) {
                                buildingBlock ??= entity.GetComponentInParent<BuildingBlock>();
                                if (buildingBlock != null) {
                                    if (buildingId == 0) buildingId = buildingBlock.buildingID;
                                    if (buildingId != buildingBlock.buildingID) continue;
                                }
                            }
                            if (buildingBlock != null) QueueConnectedBlocks(copyData, buildingBlock);
                            var transform = entity.transform;
                            if (copyData.EachToEach && copyData.ScannedPositions.Add(transform.position)) checkFrom.Push(transform.position);
                            if (entity.GetComponent<BaseLock>() != null) continue;
                            copyData.RawData.Add(EntityData(entity, transform.position, transform.rotation.eulerAngles / Mathf.Rad2Deg, copyData));
                        }
                    }
                    finally {
                        Pool.FreeUnmanaged(ref list);
                    }
                    copyData.BuildingId = buildingId;
                }
                if (checkFrom.Count > 0 && copyData.TryGetBatchYield(_config.LoopExecution.CopyBatchDelay, out var batchYield)) yield return batchYield;
            }
            var path = _subDirectory + copyData.Filename;
            var datafile = Interface.Oxide.DataFileSystem.GetFile(path);
            datafile.Clear();
            var sourcePos = copyData.SourcePos;
            datafile["default"] = new Dictionary<string, object> {
                {
                    "position", new Dictionary<string, object> {
                        {
                            "x", sourcePos.x.ToString()
                        }
                        , {
                            "y", sourcePos.y.ToString()
                        }
                        , {
                            "z", sourcePos.z.ToString()
                        }
                    }
                }
                , {
                    "rotationy", copyData.SourceRot.y.ToString()
                }
                , {
                    "rotationdiff", copyData.RotCor.ToString()
                }
            }
            ;
            datafile["entities"] = copyData.RawData;
            datafile["protocol"] = new Dictionary<string, object> {
                {
                    "items", 2
                }
                , {
                    "version", Version
                }
            }
            ;
            Interface.Oxide.DataFileSystem.SaveDatafile(path);
            copyData.Player.Reply(Lang("COPY_SUCCESS", copyData.Player.Id, copyData.Filename));
            copyData.Callback?.Invoke();
            Interface.CallHook("OnCopyFinished", copyData.RawData, copyData.Filename, copyData.Player, copyData.SourcePos);
        }
        private void QueueConnectedBlocks(CopyData copyData, BuildingBlock block) {
            var checkFrom = copyData.CheckFrom;
            var scannedPositions = copyData.ScannedPositions;
            var houseList = copyData.HouseList;
            var links = block.GetEntityLinks();
            if (links == null || links.Count == 0) return;
            for (var i = 0; i < links.Count; i++) {
                var link = links[i];
                if (link == null) continue;
                var connections = link.connections;
                if (connections == null) continue;
                for (var j = 0; j < connections.Count; j++) {
                    var neighbor = connections[j].owner as BuildingBlock;
                    if (neighbor != null && neighbor.IsValid() && !houseList.Contains(neighbor) && scannedPositions.Add(neighbor.transform.position)) checkFrom.Push(neighbor.transform.position);
                }
            }
        }
        private float DegreeToRadian(float angle) {
            return (float)(Math.PI * angle / 180.0f);
        }
        private Dictionary<string, object> EntityData(BaseEntity entity, Vector3 entPos, Vector3 entRot, CopyData copyData) {
            var normalizedPos = NormalizePosition(copyData.SourcePos, entPos, copyData.RotCor);
            var isChild = entity.HasParent();
            entRot.y -= copyData.RotCor;
            var data = new Dictionary<string, object> {
                {
                    "prefabname", entity.PrefabName
                }
                , {
                    "skinid", entity.skinID
                }
                , {
                    "flags", TryCopyFlags(entity)
                }
                , {
                    "pos", new Dictionary<string, object> {
                        {
                            "x", isChild ? entity.transform.localPosition.x.ToString() : normalizedPos.x.ToString()
                        }
                        , {
                            "y", isChild ? entity.transform.localPosition.y.ToString() : normalizedPos.y.ToString()
                        }
                        , {
                            "z", isChild ? entity.transform.localPosition.z.ToString() : normalizedPos.z.ToString()
                        }
                    }
                }
                , {
                    "rot", new Dictionary<string, object> {
                        {
                            "x", isChild ? entity.transform.localRotation.eulerAngles.x.ToString() : entRot.x.ToString()
                        }
                        , {
                            "y", isChild ? entity.transform.localRotation.eulerAngles.y.ToString() : entRot.y.ToString()
                        }
                        , {
                            "z", isChild ? entity.transform.localRotation.eulerAngles.z.ToString() : entRot.z.ToString()
                        }
                    }
                }
                , {
                    "ownerid", entity.OwnerID
                }
            }
            ;
            if (entity.networkEntityScale) {
                var scale = entity.transform.localScale;
                data.Add("scale", new Dictionary<string, object> {
                    {
                        "x", scale.x.ToString()
                    }
                    , {
                        "y", scale.y.ToString()
                    }
                    , {
                        "z", scale.z.ToString()
                    }
                }
                );
            }
            if (entity.HasParent()) {
                if (entity.parentBone != 0) {
                    data.Add("parentbone", StringPool.Get(entity.parentBone));
                }
                if (GetSlot(entity.GetParentEntity(), entity, out BaseEntity.Slot? theslot) && theslot != null) {
                    data.Add("slot", (int)theslot);
                }
            }
            if (entity.children != null && entity.children.Count > 0) {
                var children = new List<object>();
                foreach (var child in entity.children) {
                    if (!child.IsValid() || child is BasePlayer) continue;
                    children.Add(EntityData(child, child.transform.position, child.transform.rotation.eulerAngles, copyData));
                }
                if(children.Count > 0 ) data.Add("children", children);
            }
            var growableEntity = entity as GrowableEntity;
            if (growableEntity != null) {
                var genes = GrowableGeneEncoding.EncodeGenesToInt(growableEntity.Genes);
                if (genes > 0) data.Add("genes", genes);
                var previousGenes = GrowableGeneEncoding.EncodePreviousGenesToInt(growableEntity.Genes);
                if (previousGenes > 0) data.Add("previousGenes", previousGenes);
                data.Add("state", growableEntity.State);
                data.Add("totalAge", growableEntity.Age);
                data.Add("stageAge", growableEntity.stageAge);
                data.Add("yieldFraction", growableEntity.Yield);
                data.Add("yieldPool", growableEntity.yieldPool);
                data.Add("fertilized", growableEntity.Fertilized);
                var perent = growableEntity.GetParentEntity();
                if (perent != null) {
                    data.Add("hasParent", true);
                }
            }
            // TryCopySlots(entity, data, copyData.SaveShare);
            var codeLock = entity.GetComponent<CodeLock>();
            if (codeLock != null) {
                data.Add("code", codeLock.code);
                if (copyData.SaveShare) data.Add("whitelistPlayers", codeLock.whitelistPlayers);
                if (codeLock.guestCode != null && codeLock.guestCode.Length == 4) {
                    data.Add("guestCode", codeLock.guestCode);
                    if (copyData.SaveShare) data.Add("guestPlayers", codeLock.guestPlayers);
                }
            }
            var keyLock = entity.GetComponent<KeyLock>();
            if (keyLock != null) {
                data.Add("code", keyLock.keyCode.ToString());
                data.Add("firstKeyCreated", keyLock.firstKeyCreated);
            }
            var buildingblock = entity as BuildingBlock;
            if (buildingblock != null) {
                data.Add("grade", buildingblock.grade);
                if (buildingblock.customColour != 0) {
                    data.Add("customColour", buildingblock.customColour);
                }
                if (buildingblock.HasWallpaper(0)) {
                    data.Add("wallpaperID", buildingblock.wallpaperID);
                    data.Add("wallpaperHealth", buildingblock.wallpaperHealth);
                    data.Add("wallpaperRotation", buildingblock.wallpaperRotation);
                }
                if (buildingblock.HasWallpaper(1)) {
                    data.Add("wallpaperID2", buildingblock.wallpaperID2);
                    data.Add("wallpaperHealth2", buildingblock.wallpaperHealth2);
                    data.Add("wallpaperRotation2", buildingblock.wallpaperRotation2);
                }
            }
            var container = entity as IItemContainerEntity;
            if (container != null && container.inventory != null) {
                ExtractInventory(data, container.inventory, copyData);
            }
            var iSignage = entity as ISignage;
            if (iSignage != null) {
                ExtractTextures(data, iSignage.GetTextureCRCs(), entity, iSignage.FileType);
            }
            var photoEntity = entity as PhotoEntity;
            if (photoEntity != null) {
                ExtractTextures(data, photoEntity.GetContentCRCs, entity, FileStorage.Type.jpg);
            }
            var baseSculpture = entity as BaseSculpture;
            if (baseSculpture != null && baseSculpture.crc != 0) {
                var sculptureData = FileStorage.server.Get(baseSculpture.crc, FileStorage.Type.sculpt, entity.net.ID);
                if (sculptureData != null && sculptureData.Length != 0) data.Add("sculpture", Convert.ToBase64String(sculptureData));
            }
            var photoFrame = entity as PhotoFrame;
            if (photoFrame != null && photoFrame._photoEntity.uid.IsValid) {
                data.Add("photoEntity", photoFrame._photoEntity.uid.Value);
            }
            var signContent = entity as SignContent;
            if (signContent != null) {
                ExtractTextures(data, signContent.GetContentCRCs, entity, signContent.FileType);
            }
            var paintedItemStorageEntity = entity as PaintedItemStorageEntity;
            if (paintedItemStorageEntity != null) {
                ExtractTextures(data, paintedItemStorageEntity.GetContentCRCs, entity, FileStorage.Type.png);
            }
            var shutterFrame = entity as ShutterFrame;
            if (shutterFrame != null) data.Add("isShutterOpen", shutterFrame.IsShutterOpen);
            var ornateFrame = entity as OrnateFrame;
            if (ornateFrame != null) {
                data.Add("frameText", ornateFrame.FrameText);
                data.Add("textColour", SerializeUnityEngineColor(ornateFrame.TextColour));
            }
            var lights = entity as ChristmasLights;
            if (lights != null) {
                data.Add("animationStyle", lights.animationStyle);
            }
            var stringLights = entity as StringLights;
            if (stringLights != null) {
                var lightsPointsList = new List<Dictionary<string, object>>();
                foreach (var pointEntry in stringLights.points) {
                    lightsPointsList.Add(new Dictionary<string, object> {
                        {
                            "normal", pointEntry.normal
                        }
                        , {
                            "point", NormalizePosition(copyData.SourcePos, pointEntry.point, copyData.RotCor)
                        }
                        , {
                            "slack", pointEntry.slack
                        }
                        ,
                    }
                    );
                }
                data.Add("points", lightsPointsList);
            }
            var chandelier = entity as Chandelier;
            if (chandelier != null) {
                data.Add("chandelierLength", chandelier.ChandelierLength);
            }
            var orientableLight = entity as OrientableLight;
            if (orientableLight != null) {
                data.Add("pitchAmount", orientableLight.pitchAmount);
                data.Add("yawAmount", orientableLight.yawAmount);
            }
            var mannequin = entity as Mannequin;
            if (mannequin != null) {
                data.Add("poseIndex", mannequin.PoseIndex);
            }
            var partyBalloon = entity as PartyBalloon;
            if (partyBalloon != null) {
                data.Add("balloonText", partyBalloon.BalloonText);
                data.Add("balloonColour", SerializeUnityEngineColor(partyBalloon.BalloonColour));
                data.Add("textColour", SerializeUnityEngineColor(partyBalloon.TextColour));
            }
            var playerBoat = entity as PlayerBoat;
            if (playerBoat != null) {
                data.Add("lastEditLocalPos", new Dictionary<string, object> {
                    {
                        "x", playerBoat.lastEditLocalPos.x.ToString()
                    }
                    , {
                        "y", playerBoat.lastEditLocalPos.y.ToString()
                    }
                    , {
                        "z", playerBoat.lastEditLocalPos.z.ToString()
                    }
                }
                );
                data.Add("lastEditLocalRot", new Dictionary<string, object> {
                    {
                        "x", playerBoat.lastEditLocalRot.x.ToString()
                    }
                    , {
                        "y", playerBoat.lastEditLocalRot.y.ToString()
                    }
                    , {
                        "z", playerBoat.lastEditLocalRot.z.ToString()
                    }
                }
                );
            }
            if (copyData.SaveShare) {
                var sleepingBag = entity as SleepingBag;
                if (sleepingBag != null) {
                    data.Add("sleepingbag", new Dictionary<string, object> {
                        {
                            "niceName", sleepingBag.niceName
                        }
                        , {
                            "deployerUserID", sleepingBag.deployerUserID
                        }
                        , {
                            "isPublic", sleepingBag.IsPublic()
                        }
                    }
                    );
                }
                var cupboard = entity as BuildingPrivlidge;
                if (cupboard != null) {
                    data.Add("cupboard", new Dictionary<string, object> {
                        {
                            "authorizedPlayers", cupboard.authorizedPlayers.ToList()
                        }
                    }
                    );
                }
                var autoTurret = entity as AutoTurret;
                if (autoTurret != null) {
                    data.Add("autoturret", new Dictionary<string, object> {
                        {
                            "authorizedPlayers", autoTurret.authorizedPlayers.ToList()
                        }
                    }
                    );
                }
                var steeringWheel = entity as SteeringWheel;
                if (steeringWheel != null && steeringWheel.Privilege != null) {
                    var dict = new Dictionary<string, object> {
                        {
                          
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху