//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.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; #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 * */ namespace Oxide.Plugins { [Info("Copy Paste", "misticos", "4.2.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"), _groundLayer = LayerMask.GetMask("Terrain", "Default"), _rayCopy = LayerMask.GetMask("Construction", "Deployed", "Tree", "Resource", "Prevent Building"), _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 Dictionary>> _lastPastes = new Dictionary>>(); private Dictionary _signSizes = new Dictionary { //{"spinner.wheel.deployed", new SignSize(512, 512)}, { "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 ItemModAssociatedEntities = new() { typeof(PaintedItemStorageEntity), typeof(PhotoEntity), typeof(SignContent), typeof(HeadEntity), typeof(PagerEntity), typeof(MobileInventoryEntity), typeof(Cassette) }; private List _checkSlots = new List { 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; } } //Config private ConfigData _config; private class ConfigData { [JsonProperty(PropertyName = "Copy Options")] public CopyOptions Copy { get; set; } [JsonProperty(PropertyName = "Paste Options")] public PasteOptions Paste { get; set; } [JsonProperty(PropertyName = "Amount of entities to paste per batch. Use to tweak performance impact of pasting")] [DefaultValue(15)] public int PasteBatchSize = 15; [JsonProperty(PropertyName = "Amount of entities to copy per batch. Use to tweak performance impact of copying")] [DefaultValue(100)] public int CopyBatchSize = 100; [JsonProperty(PropertyName = "Amount of entities to undo per batch. Use to tweak performance impact of undoing")] [DefaultValue(15)] public int UndoBatchSize = 15; [JsonProperty(PropertyName = "Enable data saving feature")] [DefaultValue(true)] public bool DataSaving = true; 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; } } private void LoadVariables() { Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate; _config = Config.ReadObject(); Config.WriteObject(_config, true); } protected override void LoadDefaultConfig() { var configData = new ConfigData { Copy = new ConfigData.CopyOptions(), Paste = new ConfigData.PasteOptions() }; Config.WriteObject(configData, true); } //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>(); foreach (var line in _messages) { foreach (var translate in line.Value) { if (!compiledLangs.ContainsKey(translate.Key)) compiledLangs[translate.Key] = new Dictionary(); compiledLangs[translate.Key][line.Key] = translate.Value; } } foreach (var cLangs in compiledLangs) { lang.RegisterMessages(cLangs.Value, this, cLangs.Key); } } private void OnServerInitialized() { LoadVariables(); Vis.colBuffer = new Collider[8192 * 16]; JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; } #region API 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 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 callbackSpawned = null) { return TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback: callback, callbackSpawned: callbackSpawned).Item1; } private ValueTuple TryPasteFromVector3Cancellable(Vector3 pos, float rotationCorrection, string filename, string[] args, Action callback = null, Action callbackSpawned = null) { var result = TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback: callback, callbackSpawned: callbackSpawned); var pasteData = result.Item2; return new ValueTuple(result.Item1, () => pasteData.Cancelled = true); } #endregion //Other methods private object CheckCollision(HashSet> 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>(); 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 UndoLoop(HashSet entities, IPlayer player, int count = 0) { foreach (var storageContainer in entities.OfType().Where(x => !x.IsDestroyed)) { storageContainer.Kill(); } // 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. entities .Take(_config.UndoBatchSize) .ToList() .ForEach(p => { entities.Remove(p); // Cleanup the hotspot beloning to the node. var ore = p as OreResourceEntity; if (ore != null) { ore.CleanupBonus(); } var io = p as IOEntity; if (io != null) { io.ClearConnections(); } var autoTurret = p as AutoTurret; if (autoTurret != null) { AutoTurret.interferenceUpdateList.Remove(autoTurret); } if (p != null && !p.IsDestroyed) p.Kill(); }); // If it gets stuck in infinite loop break the loop. if (count != 0 && entities.Count != 0 && entities.Count == count) { player?.Reply("Undo cancelled because of infinite loop."); return; } if (entities.Count > 0) NextTick(() => UndoLoop(entities, player, entities.Count)); else if (player != null) { player.Reply(Lang("UNDO_SUCCESS", player.Id)); if (_lastPastes.ContainsKey(player.Id) && _lastPastes[player.Id].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 }; copyData.CheckFrom.Push(sourcePos); NextTick(() => 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 void CopyLoop(CopyData copyData) { var checkFrom = copyData.CheckFrom; var houseList = copyData.HouseList; var buildingId = copyData.BuildingId; var copyMechanics = copyData.CopyMechanics; 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>(); try { Vis.Entities(checkFrom.Pop(), copyData.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() != null) continue; if (!houseList.Add(entity)) continue; if (copyMechanics == CopyMechanics.Building) { var buildingBlock = entity.GetComponentInParent(); if (buildingBlock != null) { if (buildingId == 0) buildingId = buildingBlock.buildingID; if (buildingId != buildingBlock.buildingID) continue; } } var transform = entity.transform; if (copyData.EachToEach) checkFrom.Push(transform.position); if (entity.GetComponent() != 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) { NextTick(() => CopyLoop(copyData)); } else { var path = _subDirectory + copyData.Filename; var datafile = Interface.Oxide.DataFileSystem.GetFile(path); datafile.Clear(); var sourcePos = copyData.SourcePos; datafile["default"] = new Dictionary { { "position", new Dictionary { { "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 { { "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 float DegreeToRadian(float angle) { return (float)(Math.PI * angle / 180.0f); } private Dictionary 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 { { "prefabname", entity.PrefabName }, { "skinid", entity.skinID }, { "flags", TryCopyFlags(entity) }, { "pos", new Dictionary { { "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 { { "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.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(); foreach (var child in entity.children) { if (!child.IsValid()) 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 perent = growableEntity.GetParentEntity(); if (perent != null) { data.Add("hasParent", true); } } // TryCopySlots(entity, data, copyData.SaveShare); var codeLock = entity.GetComponent(); 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(); 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()) { data.Add("wallpaperID", buildingblock.wallpaperID); data.Add("wallpaperHealth", buildingblock.wallpaperHealth); } } 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 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 lights = entity as AdvancedChristmasLights; if (lights != null) { var lightsPointsList = new List>(); foreach (var lightsPoint in lights.points) { lightsPointsList.Add(new Dictionary { { "normal", lightsPoint.normal }, { "point", NormalizePosition(copyData.SourcePos, lightsPoint.point, copyData.RotCor) }, { "slack", lightsPoint.slack }, }); } data.Add("points", lightsPointsList); data.Add("animationStyle", lights.animationStyle); } if (copyData.SaveShare) { var sleepingBag = entity as SleepingBag; if (sleepingBag != null) { data.Add("sleepingbag", new Dictionary { { "niceName", sleepingBag.niceName }, { "deployerUserID", sleepingBag.deployerUserID }, { "isPublic", sleepingBag.IsPublic() } }); } var cupboard = entity as BuildingPrivlidge; if (cupboard != null) { data.Add("cupboard", new Dictionary { { "authorizedPlayers", cupboard.authorizedPlayers.Select(y => y.userid).ToList() } }); } var autoTurret = entity as AutoTurret; if (autoTurret != null) { data.Add("autoturret", new Dictionary { { "authorizedPlayers", autoTurret.authorizedPlayers.Select(p => p.userid).ToList() } }); } } var firework = entity as PatternFirework; if (firework != null && firework?.Design != null && firework?.Design?.stars != null) { data.Add("patternfirework", new Dictionary { { "editedBy", firework.Design.editedBy }, { "stars", SerializeStarPattern(firework.Design.stars) }, }); } var tinCanAlarm = entity as TinCanAlarm; if (tinCanAlarm != null) { if (tinCanAlarm.endPoint != Vector3.zero) { data.Add("endPoint", NormalizePosition(copyData.SourcePos, tinCanAlarm.endPoint, copyData.RotCor)); } } var cctvRc = entity as CCTV_RC; if (cctvRc != null) { data.Add("cctv", new Dictionary { { "yaw", cctvRc.yawAmount }, { "pitch", cctvRc.pitchAmount }, { "rcIdentifier", cctvRc.rcIdentifier } }); } var headEntity = entity as HeadEntity; if (headEntity != null) { ExtractHeadData(data, headEntity.CurrentTrophyData); } var huntingTrophy = entity as HuntingTrophy; if (huntingTrophy != null) { ExtractHeadData(data, huntingTrophy.CurrentTrophyData); } // Save RF frequencies for non-IOEntity RF objects (IOEntity frequencies are handled separately) var rfObject = entity as IRFObject; if (rfObject != null && entity is not IOEntity) { var frequency = rfObject.GetFrequency(); if (frequency > 0) data.Add("frequency", frequency); } var cassette = entity as Cassette; if (cassette != null) { ExtractCassette(data, cassette); } var elevator = entity as Elevator; if (elevator != null) { data.Add("Floor", elevator.Floor); } var weaponRack = entity as WeaponRack; if (weaponRack != null) { var gridSlots = new List(); foreach (var weaponRackGridSlot in weaponRack.gridSlots) { if (weaponRackGridSlot != null && weaponRackGridSlot.Used) { gridSlots.Add(new Dictionary { { "GridSlotIndex", weaponRackGridSlot.GridSlotIndex }, { "InventoryIndex", weaponRackGridSlot.InventoryIndex }, { "Rotation", weaponRackGridSlot.Rotation }, }); } } data.Add("gridSlots", gridSlots); } var phoneController = entity.GetComponent(); if (phoneController != null && phoneController.savedVoicemail != null && phoneController.savedVoicemail.Count > 0) { var savedVoicemail = new List(); foreach (var voicemail in phoneController.savedVoicemail) { var oggByte = FileStorage.server.Get(voicemail.audioId, FileStorage.Type.ogg, entity.net.ID); if (oggByte != null) { savedVoicemail.Add(new Dictionary { { "audio", Convert.ToBase64String(oggByte) }, { "userName", voicemail.userName } }); } } data.Add("savedVoicemail", savedVoicemail); } var vendingMachine = entity as VendingMachine; if (vendingMachine != null) { var sellOrders = new List(); foreach (var vendItem in vendingMachine.sellOrders.sellOrders) { sellOrders.Add(new Dictionary { { "itemToSellID", vendItem.itemToSellID }, { "itemToSellAmount", vendItem.itemToSellAmount }, { "currencyID", vendItem.currencyID }, { "currencyAmountPerItem", vendItem.currencyAmountPerItem }, { "inStock", vendItem.inStock }, { "currencyIsBP", vendItem.currencyIsBP }, { "itemToSellIsBP", vendItem.itemToSellIsBP } }); } data.Add("vendingmachine", new Dictionary { { "shopName", vendingMachine.shopName }, { "isBroadcasting", vendingMachine.IsBroadcasting() }, { "sellOrders", sellOrders } }); } var ioEntity = entity as IOEntity; if (ioEntity.IsValid() && !ioEntity.IsDestroyed) { var ioData = new Dictionary(); var inputs = ioEntity.inputs.Select(input => new Dictionary { { "connectedID", input.connectedTo.entityRef.uid.Value }, { "connectedToSlot", input.connectedToSlot }, { "niceName", input.niceName }, { "wireColour", input.wireColour }, { "type", (int)input.type } }) .Cast() .ToList(); ioData.Add("inputs", inputs); var outputs = new List(); foreach (var output in ioEntity.outputs) { var ioConnection = new Dictionary { { "connectedID", output.connectedTo.entityRef.uid.Value }, { "connectedToSlot", output.connectedToSlot }, { "niceName", output.niceName }, { "wireColour", output.wireColour }, { "type", (int)output.type }, { "linePoints", output.linePoints?.ToList() ?? new List() }, { "lineAnchors", output.lineAnchors != null ? GetLineAnchors(output.lineAnchors, ioEntity) : new List() }, { "slackLevels", output.slackLevels?.ToList() ?? new List() }, }; outputs.Add(ioConnection); } ioData.Add("outputs", outputs); ioData.Add("oldID", ioEntity.net.ID.Value); var electricalBranch = ioEntity as ElectricalBranch; if (electricalBranch != null) { ioData.Add("branchAmount", electricalBranch.branchAmount); } var counter = ioEntity as PowerCounter; if (counter != null) { ioData.Add("targetNumber", counter.GetTarget()); ioData.Add("counterNumber", counter.counterNumber); } var timerSwitch = ioEntity as TimerSwitch; if (timerSwitch != null) { ioData.Add("timerLength", timerSwitch.timerLength); } var rfBroadcaster = ioEntity as IRFObject; if (rfBroadcaster != null) { ioData.Add("frequency", rfBroadcaster.GetFrequency()); } var seismicSensor = ioEntity as SeismicSensor; if (seismicSensor != null) { ioData.Add("range", seismicSensor.range); } var conveyor = ioEntity as IndustrialConveyor; if (conveyor != null) { ioData.Add("industrialconveyormode", (int)conveyor.mode); ioData.Add("industrialconveyorfilteritems", SerializeConveyorFilter(conveyor.filterItems)); } var digitalClock = ioEntity as DigitalClock; if (digitalClock != null) { var alarms = new List(); foreach (var alarm in digitalClock.alarms) { alarms.Add(new Dictionary { { "time", alarm.time }, { "active", alarm.active }, }); } ioData.Add("muted", digitalClock.muted); ioData.Add("alarms", alarms); } data.Add("IOEntity", ioData); } if (entity is StorageContainer || entity is Door || (isChild && entity is not IOEntity)) { data.Add("oldID", entity.net.ID.Value); } return data; } private List GetLineAnchors(IOEntity.LineAnchor[] lineAnchors, IOEntity ioEntity) { var anchors = new List(); foreach (var anchor in lineAnchors) { anchors.Add(new Dictionary { { "position", new Dictionary { { "x", anchor.position.x.ToString() }, { "y", anchor.position.y.ToString() }, { "z", anchor.position.z.ToString() } } }, { "index", anchor.index }, { "boneName", anchor.boneName }, { "entityRefID", anchor.entityRef.uid.Value } }); } return anchors; } private object FindBestHeight(HashSet> entities, Vector3 startPos) { var maxHeight = 0f; foreach (var entity in entities) { var prefab = (string)entity["prefabname"]; if (prefab.Contains("/foundation/") || prefab.Contains("/foundation.triangle/")) { var foundHeight = GetGround((Vector3)entity["position"]); if (foundHeight != null) { var height = (Vector3)foundHeight; if (height.y > maxHeight) maxHeight = height.y; } } } maxHeight += 1f; return maxHeight; } private bool FindRayEntity(Vector3 sourcePos, Vector3 sourceDir, out Vector3 point, out BaseEntity entity, int rayLayer) { RaycastHit hitinfo; entity = null; point = Vector3.zero; if (!Physics.Raycast(sourcePos, sourceDir, out hitinfo, 1000f, rayLayer)) return false; entity = hitinfo.GetEntity(); point = hitinfo.point; return true; } private byte[] FixSignage(ISignage iSignage, byte[] imageBytes) { var sign = iSignage as Signage; if (sign == null || !_signSizes.ContainsKey(sign.ShortPrefabName)) return imageBytes; var size = Math.Max(sign.paintableSources.Length, 1); if (sign.textureIDs == null || sign.textureIDs.Length != size) { Array.Resize(ref sign.textureIDs, size); } return ImageResize(imageBytes, _signSizes[sign.ShortPrefabName].Width, _signSizes[sign.ShortPrefabName].Height); } private object GetGround(Vector3 pos) { RaycastHit hitInfo; pos += new Vector3(0, 100, 0); if (Physics.Raycast(pos, Vector3.down, out hitInfo, 200, _groundLayer)) return hitInfo.point; return null; } private int GetItemId(int itemId) { if (ReplaceItemId.ContainsKey(itemId)) return ReplaceItemId[itemId]; return itemId; } private bool HasAccess(IPlayer player, string permName) { return player.IsAdmin || player.HasPermission(permName); } private byte[] ImageResize(byte[] imageBytes, int width, int height) { Bitmap resizedImage = new Bitmap(width, height), sourceImage = new Bitmap(new MemoryStream(imageBytes)); Graphics.FromImage(resizedImage).DrawImage(sourceImage, new Rectangle(0, 0, width, height), new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel); var ms = new MemoryStream(); resizedImage.Save(ms, ImageFormat.Png); return ms.ToArray(); } private string Lang(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args); private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float diffRot) { var transformedPos = currentPos - initialPos; var newX = transformedPos.x * (float)Math.Cos(-diffRot) + transformedPos.z * (float)Math.Sin(-diffRot); var newZ = transformedPos.z * (float)Math.Cos(-diffRot) - transformedPos.x * (float)Math.Sin(-diffRot); transformedPos.x = newX; transformedPos.z = newZ; return transformedPos; } private PasteData Paste(ICollection> entities, Dictionary protocol, bool ownership, Vector3 startPos, IPlayer player, bool stability, float rotationCorrection, float heightAdj, bool auth, Action callback, Action callbackSpawned, string filename, bool checkPlaced, bool enableSaving = true) { //Settings var isItemReplace = !protocol.ContainsKey("items"); var eulerRotation = new Vector3(0f, rotationCorrection * Mathf.Rad2Deg, 0f); var quaternionRotation = Quaternion.Euler(eulerRotation); // Parse VersionNumber var version = protocol.ContainsKey("version") ? protocol["version"] as Dictionary : null; VersionNumber vNumber = default; if (version != null) vNumber = new VersionNumber((int)version["Major"], (int)version["Minor"], (int)version["Patch"]); var pasteData = new PasteData { HeightAdj = heightAdj, IsItemReplace = isItemReplace, Entities = entities, Player = player, BasePlayer = player.Object as BasePlayer, QuaternionRotation = quaternionRotation, StartPos = startPos, Stability = stability, Auth = auth, Ownership = ownership, CallbackFinished = callback, CallbackSpawned = callbackSpawned, Filename = filename, CheckPlaced = checkPlaced, Version = vNumber, EnableSaving = enableSaving }; NextTick(() => PasteLoop(pasteData)); return pasteData; } private void PasteLoop(PasteData pasteData) { if (pasteData.Cancelled) { UndoLoop(new HashSet(pasteData.PastedEntities), pasteData.Player, pasteData.PastedEntities.Count); return; } var entities = pasteData.Entities; var todo = entities.Take(_config.PasteBatchSize).ToArray(); foreach (var data in todo) { entities.Remove(data); PasteEntity(data, pasteData); } if (entities.Count > 0) NextTick(() => PasteLoop(pasteData)); else { foreach (var ioData in pasteData.EntityLookup.Values.ToArray()) { ProgressIOEntity(ioData, pasteData); } foreach (var keyPair in pasteData.ItemsWithSubEntity) { SetItemSubEntity(pasteData, keyPair.Value, keyPair.Key); } foreach (var entity in pasteData.StabilityEntities) { entity.grounded = false; entity.InitializeSupports(); entity.UpdateStability(); } foreach (var adapter in pasteData.industrialStorageAdaptors) { if (adapter == null) { continue; } if (!adapter.HasParent()) { List ents = Facepunch.Pool.Get>(); Vis.Entities(adapter.transform.position + (adapter.transform.up * -0.2f), 0.01f, ents); if (ents.Count > 0) { adapter.SetParent(ents[0], true, true); } Facepunch.Pool.FreeUnmanaged(ref ents); } adapter.MarkDirtyForceUpdateOutputs(); adapter.SendNetworkUpdateImmediate(false); adapter.RefreshIndustrialPreventBuilding(); adapter.NotifyIndustrialNetworkChanged(); } pasteData.FinalProcessingActions.ForEach(action => action()); pasteData.Player.Reply(Lang("PASTE_SUCCESS", pasteData.Player.Id)); #if DEBUG pasteData.Player.Reply($"Stopwatch took: {pasteData.Sw.Elapsed.TotalMilliseconds} ms"); #endif if (!_lastPastes.ContainsKey(pasteData.Player.Id)) _lastPastes[pasteData.Player.Id] = new Stack>(); _lastPastes[pasteData.Player.Id].Push(pasteData.PastedEntities); pasteData.CallbackFinished?.Invoke(); Interface.CallHook("OnPasteFinished", pasteData.PastedEntities, pasteData.Filename, pasteData.Player, pasteData.StartPos); } } private void PasteEntity(Dictionary data, PasteData pasteData, BaseEntity parent = null) { bool isChild = parent != null; var prefabname = (string)data["prefabname"]; #if DEBUG Puts($"{nameof(PasteLoop)}: Entity {prefabname}"); #endif var skinid = ulong.Parse(data["skinid"].ToString()); var pos = isChild ? Vector3.zero : (Vector3)data["position"]; var rot = isChild ? Quaternion.identity : (Quaternion)data["rotation"]; var localPos = isChild ? (Vector3)data["position"] : Vector3.zero; var localRot = isChild ? (Quaternion)data["rotation"] : Quaternion.identity; var ownerId = pasteData.BasePlayer?.userID ?? 0; if (data.ContainsKey("ownerid")) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1077"); #endif ownerId = Convert.ToUInt64(data["ownerid"]); } if (pasteData.CheckPlaced && CheckPlaced(prefabname, pos, rot)) return; if (prefabname.Contains("pillar")) return; // Used to copy locks for no reason in previous versions (is included in the slots info so no need to copy locks) so just skipping them. if (prefabname.Contains("locks") && pasteData.Version < new VersionNumber(4, 2, 0)) return; BaseEntity entity = null; // Check to see if this child is already spawned if (isChild && parent.children != null) { foreach (var child in parent.children) { if (child == null || child.IsDestroyed) continue; // Skip associated entities that can have multiple instances and always have localPosition set to Vector3.zero if (ItemModAssociatedEntities.Contains(child.GetType()) && child.transform.localPosition == Vector3.zero) continue; if (child.PrefabName == prefabname && (child.transform.localPosition - localPos).sqrMagnitude < 0.001f) { entity = child; break; } } } if (entity == null) entity = GameManager.server.CreateEntity(prefabname, pos, rot); if (entity == null) return; var transform = entity.transform; // If the entity is a child, set the parent and the local position and rotation. if (isChild) { if (!entity.isSpawned) { entity.gameObject.Identity(); if (data.ContainsKey("parentbone")) entity.SetParent(parent, data["parentbone"].ToString()); else entity.SetParent(parent); // Custom door controllers and auto turrets don't have null checks for deployedBy baseplayer if (entity is not CustomDoorManipulator && entity is not AutoTurret) entity.OnDeployed(parent, null, _emptyItem); transform.localPosition = localPos; transform.localRotation = localRot; } } // If the entity is not a child, set the position and rotation. else { transform.position = pos; transform.rotation = rot; } if (pasteData.BasePlayer != null) entity.SendMessage("SetDeployedBy", pasteData.BasePlayer, SendMessageOptions.DontRequireReceiver); if (pasteData.Ownership) entity.OwnerID = ownerId; var buildingBlock = entity as BuildingBlock; if (buildingBlock != null) { buildingBlock.blockDefinition = PrefabAttribute.server.Find(buildingBlock.prefabID); var grade = (BuildingGrade.Enum)Convert.ToInt32(data["grade"]); if (skinid != 0ul && !HasGrade(buildingBlock, grade, skinid)) skinid = 0ul; buildingBlock.SetGrade(grade); if (!pasteData.Stability) buildingBlock.grounded = true; } var decayEntity = entity as DecayEntity; if (decayEntity != null) { if (pasteData.BuildingId == 0) pasteData.BuildingId = BuildingManager.server.NewBuildingID(); decayEntity.AttachToBuilding(pasteData.BuildingId); } var stabilityEntity = entity as StabilityEntity; if (stabilityEntity != null) { if (!stabilityEntity.grounded) { stabilityEntity.grounded = true; pasteData.StabilityEntities.Add(stabilityEntity); } } if (data.TryGetValue("oldID", out var oldIDString)) { ulong oldID = Convert.ToUInt64(oldIDString); if (!pasteData.EntityLookup.ContainsKey(oldID)) { pasteData.EntityLookup.Add(oldID, new Dictionary { { "entity", entity } }); } } entity.skinID = skinid; if (!pasteData.EnableSaving) { entity.EnableSaving(false); } if (entity is ModularCar modularCar && data.TryGetValue("children", out var childrenObj)) { // If there are children present, disable the default spawn settings to prevent stacking modules // on top of each other, which could cause the vehicle to be destroyed var children = childrenObj as List; if (children != null && children.Count > 0) modularCar.spawnSettings.useSpawnSettings = false; } if (!entity.isSpawned) entity.Spawn(); if (entity.net == null || entity.IsDestroyed) return; var baseCombat = entity as BaseCombatEntity; if (buildingBlock != null) { buildingBlock.SetHealthToMax(); buildingBlock.UpdateSkin(); buildingBlock.SendNetworkUpdate(); buildingBlock.ResetUpkeepTime(); object customColour; if (data.TryGetValue("customColour", out customColour)) buildingBlock.SetCustomColour(Convert.ToUInt32(customColour)); object wallpaperHealth; if (data.TryGetValue("wallpaperHealth", out wallpaperHealth)) buildingBlock.wallpaperHealth = Convert.ToInt32(wallpaperHealth); object wallpaperID; if (data.TryGetValue("wallpaperID", out wallpaperID)) buildingBlock.SetWallpaper(Convert.ToUInt64(wallpaperID)); } else if (baseCombat != null) baseCombat.SetHealth(baseCombat.MaxHealth()); var firework = entity as PatternFirework; if (firework != null && data.ContainsKey("patternfirework")) { if (firework?.Design == null) { firework.Design = new ProtoBuf.PatternFirework.Design(); firework.Design.stars = new List(); } var pattern = (Dictionary)data["patternfirework"]; object editedBy; if (data.TryGetValue("editedBy", out editedBy)) firework.Design.editedBy = Convert.ToUInt32(pattern["editedBy"]); firework.Design.stars = DeSerializeStarPattern(pattern["stars"].ToString()); firework.SendNetworkUpdate(); } // This needs to stay for the old configs to load properly but is unused because of the new 'children' system. pasteData.PastedEntities.AddRange(TryPasteSlots(entity, data, pasteData)); if (isChild && data.ContainsKey( "slot" )) { var slot = (BaseEntity.Slot) Convert.ToInt32(data["slot"]); if (parent.HasSlot( slot )) { parent.SetSlot( slot, entity ); } } TryPasteLocks(entity, data, pasteData); var autoTurret = entity as AutoTurret; if (autoTurret != null) { var authorizedPlayers = new List(); if (data.ContainsKey("autoturret")) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1305"); #endif var autoTurretData = data["autoturret"] as Dictionary; authorizedPlayers = (autoTurretData["authorizedPlayers"] as List) .Select(Convert.ToUInt64).ToList(); } if (pasteData.BasePlayer != null && !authorizedPlayers.Contains(pasteData.BasePlayer.userID) && pasteData.Auth) authorizedPlayers.Add(pasteData.BasePlayer.userID); foreach (var userId in authorizedPlayers) { autoTurret.authorizedPlayers.Add(new PlayerNameID { userid = userId, username = "Player" }); } autoTurret.SendNetworkUpdate(); } var box = entity as IItemContainerEntity; if (box != null) { if (box.inventory == null) { if (entity is StorageContainer storageContainer) { storageContainer.CreateInventory(true); } else if (entity is IndustrialCrafter crafter) { crafter.CreateInventory(true); } else if (entity is ContainerIOEntity containerIo) { containerIo.CreateInventory(true); containerIo.OnInventoryFirstCreated(containerIo.inventory); } else { Puts("WARNING: New IItemContainerEntity container '{0}' not supported", entity); } } else { box.inventory.Clear(); } if (box.inventory != null) { PopulateInventory(pasteData, data, entity, box.inventory); if (autoTurret != null) { autoTurret.UpdateAttachedWeapon(); } entity.SendNetworkUpdate(); } } var iSignage = entity as ISignage; if (iSignage != null && data.ContainsKey("sign")) { var signData = data["sign"] as Dictionary; if (signData.ContainsKey("amount") || signData.ContainsKey("texture") || signData.ContainsKey("texture0")) { int amount = int.TryParse(signData["amount"].ToString(), out var parsedAmount) ? parsedAmount : 1; uint[] newTextureIDs = new uint[amount]; for (var num = 0; num < amount; num++) { string textureKey = $"texture{num}"; if (amount == 1 && signData.ContainsKey("texture")) { textureKey = "texture"; } if (signData.ContainsKey(textureKey)) { var imageBytes = FixSignage(iSignage, Convert.FromBase64String(signData[textureKey].ToString())); newTextureIDs[num] = FileStorage.server.Store(imageBytes, iSignage.FileType, entity.net.ID); } else { newTextureIDs[num] = 0; } } iSignage.SetTextureCRCs(newTextureIDs); } if (iSignage is Signage sign) { if (Convert.ToBoolean(signData["locked"])) sign.SetFlag(BaseEntity.Flags.Locked, true); sign.SendNetworkUpdate(); } } var photoEntity = entity as PhotoEntity; if (photoEntity != null && data.ContainsKey("sign")) { var signData = data["sign"] as Dictionary; if (signData.ContainsKey("amount")) { int amount; if (int.TryParse(signData["amount"].ToString(), out amount)) { for (var num = 0; num < amount; num++) { string textureKey = $"texture{num}"; if (signData.ContainsKey(textureKey)) { var imageBytes = Convert.FromBase64String(signData[textureKey].ToString()); photoEntity.SetImageData(0UL, imageBytes); } } } } } var signContent = entity as SignContent; if (signContent != null && data.ContainsKey("sign")) { var signData = data["sign"] as Dictionary; if (signData.ContainsKey("amount")) { int amount; if (int.TryParse(signData["amount"].ToString(), out amount)) { uint[] newTextureIDs = new uint[amount]; for (var num = 0; num < amount; num++) { string textureKey = $"texture{num}"; if (signData.ContainsKey(textureKey)) { var imageBytes = Convert.FromBase64String(signData[textureKey].ToString()); newTextureIDs[num] = FileStorage.server.Store(imageBytes, signContent.FileType, entity.net.ID); } else { newTextureIDs[num] = 0; } } signContent.textureIDs = newTextureIDs; } } } var paintedItemStorageEntity = entity as PaintedItemStorageEntity; if (paintedItemStorageEntity != null && data.ContainsKey("sign")) { var signData = data["sign"] as Dictionary; if (signData.ContainsKey("amount")) { int amount; if (int.TryParse(signData["amount"].ToString(), out amount)) { for (var num = 0; num < amount; num++) { if (signData.ContainsKey($"texture{num}")) { var imageBytes = Convert.FromBase64String(signData[$"texture{num}"].ToString()); if (ImageProcessing.IsValidPNG(imageBytes, 512, 512)) paintedItemStorageEntity._currentImageCrc = FileStorage.server.Store(imageBytes, FileStorage.Type.png, paintedItemStorageEntity.net.ID); } } } } } var lights = entity as AdvancedChristmasLights; if (lights != null) { if (data.ContainsKey("points")) { var points = data["points"] as List; if (points != null && points.Count > 0) { foreach (Dictionary pointEntry in points) { var normal = (Dictionary)pointEntry["normal"]; var point = (Dictionary)pointEntry["point"]; var adjustedPoint = pasteData.QuaternionRotation * new Vector3(Convert.ToSingle(point["x"]), Convert.ToSingle(point["y"]), Convert.ToSingle(point["z"])) + pasteData.StartPos; adjustedPoint.y += pasteData.HeightAdj; float slack = 0f; if (pointEntry.TryGetValue("slack", out var rawSlack)) slack = Convert.ToSingle(rawSlack); lights.points.Add(new AdvancedChristmasLights.pointEntry { normal = new Vector3(Convert.ToSingle(normal["x"]), Convert.ToSingle(normal["y"]), Convert.ToSingle(normal["z"])), point = adjustedPoint, slack = slack }); } } } if (data.ContainsKey("animationStyle")) { lights.animationStyle = (AdvancedChristmasLights.AnimationType)data["animationStyle"]; } } var sleepingBag = entity as SleepingBag; if (sleepingBag != null && data.ContainsKey("sleepingbag")) { var bagData = data["sleepingbag"] as Dictionary; sleepingBag.niceName = bagData["niceName"].ToString(); sleepingBag.deployerUserID = ulong.Parse(bagData["deployerUserID"].ToString()); sleepingBag.SetPublic(Convert.ToBoolean(bagData["isPublic"])); } var cupboard = entity as BuildingPrivlidge; if (cupboard != null) { var authorizedPlayers = new List(); if (data.ContainsKey("cupboard")) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1521"); #endif var cupboardData = data["cupboard"] as Dictionary; authorizedPlayers = (cupboardData["authorizedPlayers"] as List).Select(Convert.ToUInt64) .ToList(); } if (pasteData.BasePlayer != null && !authorizedPlayers.Contains(pasteData.BasePlayer.userID) && pasteData.Auth) authorizedPlayers.Add(pasteData.BasePlayer.userID); foreach (var userId in authorizedPlayers) { cupboard.authorizedPlayers.Add(new PlayerNameID { userid = userId, username = "Player" }); } cupboard.SendNetworkUpdate(); } var tinCanAlarm = entity as TinCanAlarm; if (tinCanAlarm != null) { if (data.ContainsKey("endPoint")) { var endPoint = (Dictionary)data["endPoint"]; var adjustedEndPoint = pasteData.QuaternionRotation * new Vector3(Convert.ToSingle(endPoint["x"]), Convert.ToSingle(endPoint["y"]), Convert.ToSingle(endPoint["z"])) + pasteData.StartPos; adjustedEndPoint.y += pasteData.HeightAdj; tinCanAlarm.endPoint = adjustedEndPoint; tinCanAlarm.SendNetworkUpdate(); } } var cctvRc = entity as CCTV_RC; if (cctvRc != null && data.ContainsKey("cctv")) { var cctv = (Dictionary)data["cctv"]; cctvRc.yawAmount = Convert.ToSingle(cctv["yaw"]); cctvRc.pitchAmount = Convert.ToSingle(cctv["pitch"]); cctvRc.rcIdentifier = cctv["rcIdentifier"].ToString(); cctvRc.SendNetworkUpdate(); } var headEntity = entity as HeadEntity; if (headEntity != null && data.ContainsKey("currentTrophyData")) { if (headEntity.CurrentTrophyData == null) headEntity.CurrentTrophyData = Pool.Get(); PopulateHeadData(data, headEntity.CurrentTrophyData); } var huntingTrophy = entity as HuntingTrophy; if (huntingTrophy != null && data.ContainsKey("currentTrophyData")) { if (huntingTrophy.CurrentTrophyData == null) huntingTrophy.CurrentTrophyData = Pool.Get(); PopulateHeadData(data, huntingTrophy.CurrentTrophyData); } var pagerEntity = entity as PagerEntity; if (pagerEntity != null && data.ContainsKey("frequency")) { pagerEntity.ChangeFrequency(Convert.ToInt32(data["frequency"])); pagerEntity.SendNetworkUpdate(); } var rfTimedExplosive = entity as RFTimedExplosive; if (rfTimedExplosive != null && data.ContainsKey("frequency")) { rfTimedExplosive.SetFrequency(Convert.ToInt32(data["frequency"])); rfTimedExplosive.SetFuse(0f); } var cassette = entity as Cassette; if (cassette != null) { PopulateCassette(data, cassette); } var mobileInventoryEntity = entity as MobileInventoryEntity; if (mobileInventoryEntity != null) { mobileInventoryEntity.SetFlag(MobileInventoryEntity.Ringing, false); } var elevator = entity as Elevator; if (elevator != null && data.ContainsKey("Floor")) { elevator.Floor = Convert.ToInt32(data["Floor"]); } var weaponRack = entity as WeaponRack; if (weaponRack != null && data.ContainsKey("gridSlots")) { var gridSlots = (List)data["gridSlots"]; if (gridSlots != null) { foreach (Dictionary gridSlot in gridSlots) { if (gridSlot != null) { int gridSlotIndex = Convert.ToInt32(gridSlot["GridSlotIndex"]); int inventoryIndex = Convert.ToInt32(gridSlot["InventoryIndex"]); int rotation = Convert.ToInt32(gridSlot["Rotation"]); var item = weaponRack.inventory.GetSlot(inventoryIndex); if (item != null) { var slot = weaponRack.gridSlots[inventoryIndex]; if (slot != null) { slot.SetItem(item, item.info, gridSlotIndex, rotation); weaponRack.SetGridCellContents(slot, false); } } } } } } var phoneController = entity.GetComponent(); if (phoneController != null && data.ContainsKey("savedVoicemail")) { // Requires the cassette item subentity to exist, delay processing until the very end pasteData.FinalProcessingActions.Add(() => { var savedVoicemail = (List)data["savedVoicemail"]; if (savedVoicemail != null) { foreach (Dictionary voicemail in savedVoicemail) { if (voicemail != null) { byte[] audioData = Convert.FromBase64String(voicemail["audio"].ToString()); phoneController.SaveVoicemail(audioData, voicemail["userName"].ToString()); } } } }); } var vendingMachine = entity as VendingMachine; if (vendingMachine != null && data.ContainsKey("vendingmachine")) { var vendingData = data["vendingmachine"] as Dictionary; vendingMachine.shopName = vendingData["shopName"].ToString(); vendingMachine.SetFlag(BaseEntity.Flags.Reserved4, Convert.ToBoolean(vendingData["isBroadcasting"])); var sellOrders = vendingData["sellOrders"] as List; foreach (var orderPreInfo in sellOrders) { var orderInfo = orderPreInfo as Dictionary; if (!orderInfo.ContainsKey("inStock")) { orderInfo["inStock"] = 0; orderInfo["currencyIsBP"] = false; orderInfo["itemToSellIsBP"] = false; } int itemToSellId = Convert.ToInt32(orderInfo["itemToSellID"]), currencyId = Convert.ToInt32(orderInfo["currencyID"]); if (pasteData.IsItemReplace) { itemToSellId = GetItemId(itemToSellId); currencyId = GetItemId(currencyId); } vendingMachine.sellOrders.sellOrders.Add(new ProtoBuf.VendingMachine.SellOrder { ShouldPool = false, itemToSellID = itemToSellId, itemToSellAmount = Convert.ToInt32(orderInfo["itemToSellAmount"]), currencyID = currencyId, currencyAmountPerItem = Convert.ToInt32(orderInfo["currencyAmountPerItem"]), inStock = Convert.ToInt32(orderInfo["inStock"]), currencyIsBP = Convert.ToBoolean(orderInfo["currencyIsBP"]), itemToSellIsBP = Convert.ToBoolean(orderInfo["itemToSellIsBP"]) }); } vendingMachine.FullUpdate(); } var ioEntity = entity as IOEntity; if (ioEntity.IsValid() && !ioEntity.IsDestroyed) { var ioData = new Dictionary(); if (data.ContainsKey("IOEntity")) { ioData = data["IOEntity"] as Dictionary ?? new Dictionary(); } ioData.Add("entity", ioEntity); ioData.Add("newId", ioEntity.net.ID.Value); object oldIdObject; if (ioData.TryGetValue("oldID", out oldIdObject)) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1619"); #endif var oldId = Convert.ToUInt64(oldIdObject); pasteData.EntityLookup.Add(oldId, ioData); } } var flagsData = new Dictionary(); if (data.ContainsKey("flags")) flagsData = data["flags"] as Dictionary; var flags = new Dictionary(); foreach (var flagData in flagsData) { BaseEntity.Flags baseFlag; if (Enum.TryParse(flagData.Key, out baseFlag)) flags.Add(baseFlag, Convert.ToBoolean(flagData.Value)); } foreach (var flag in flags) { entity.SetFlag(flag.Key, flag.Value); } var industrialCrafter = entity as IndustrialCrafter; if (industrialCrafter != null) { industrialCrafter.SetFlag(IndustrialCrafter.Crafting, false); } if (data.ContainsKey("children")) { var children = data["children"] as List; if (children != null) { foreach (var child in children) { var childData = child as Dictionary; if (childData == null) continue; PasteEntity(childData, pasteData, entity); } } } var photoFrame = entity as PhotoFrame; if (photoFrame != null && data.ContainsKey("photoEntity")) { if (pasteData.EntityLookup.TryGetValue(Convert.ToUInt64(data["photoEntity"]), out var objData)) { if (objData["entity"] is BaseEntity baseEntity && baseEntity.IsValid() && !baseEntity.IsDestroyed && baseEntity.net.ID.IsValid) { photoFrame._photoEntity.uid = baseEntity.net.ID; } } } if (entity is IndustrialStorageAdaptor) { pasteData.industrialStorageAdaptors.Add(entity as IndustrialStorageAdaptor); } pasteData.PastedEntities.Add(entity); pasteData.CallbackSpawned?.Invoke(entity); } private void ProgressIOEntity(Dictionary ioData, PasteData pasteData) { if (!ioData.ContainsKey("entity")) return; var ioEntity = ioData["entity"] as IOEntity; if (!ioEntity.IsValid() || ioEntity.IsDestroyed) return; List inputs = null; if (ioData.ContainsKey("inputs")) inputs = ioData["inputs"] as List; var electricalBranch = ioEntity as ElectricalBranch; if (electricalBranch != null && ioData.ContainsKey("branchAmount")) { electricalBranch.branchAmount = Convert.ToInt32(ioData["branchAmount"]); } var counter = ioEntity as PowerCounter; if (counter != null) { if (ioData.ContainsKey("targetNumber")) counter.targetCounterNumber = Convert.ToInt32(ioData["targetNumber"]); object counterNumber; counter.SetCounterNumber(ioData.TryGetValue("counterNumber", out counterNumber) ? Convert.ToInt32(counterNumber) : 0); } var timerSwitch = ioEntity as TimerSwitch; if (timerSwitch != null && ioData.ContainsKey("timerLength")) { timerSwitch.timerLength = Convert.ToSingle(ioData["timerLength"]); if(timerSwitch.IsOn()) { timerSwitch.SetFlag(BaseEntity.Flags.On, false); timerSwitch.SwitchPressed(); } } var rfBroadcaster = ioEntity as RFBroadcaster; if (rfBroadcaster != null && ioData.ContainsKey("frequency")) { int newFrequency = Convert.ToInt32(ioData["frequency"]); if (ioEntity.IsPowered()) RFManager.AddBroadcaster(newFrequency, rfBroadcaster); rfBroadcaster.frequency = newFrequency; rfBroadcaster.MarkDirty(); } var rfReceiver = ioEntity as RFReceiver; if (rfReceiver != null && ioData.ContainsKey("frequency")) { int newFrequency = Convert.ToInt32(ioData["frequency"]); RFManager.AddListener(newFrequency, rfReceiver); rfReceiver.frequency = newFrequency; rfReceiver.MarkDirty(); } var seismicSensor = ioEntity as SeismicSensor; if (seismicSensor != null && ioData.ContainsKey("range")) { seismicSensor.SetRange(Convert.ToInt32(ioData["range"])); } var doorManipulator = ioEntity as CustomDoorManipulator; if (doorManipulator != null) { Door door = doorManipulator.GetParentEntity() as Door; if (door != null) { doorManipulator.SetTargetDoor(door); } } var conveyor = ioEntity as IndustrialConveyor; if (conveyor != null && ioData.ContainsKey("industrialconveyormode")) { object mode; if (ioData.TryGetValue("industrialconveyormode", out mode)) conveyor.mode = (IndustrialConveyor.ConveyorMode)Convert.ToInt32(mode); conveyor.filterItems = DeSerializeConveyorFilter(ioData["industrialconveyorfilteritems"].ToString()); conveyor.SendNetworkUpdate(); } var digitalClock = ioEntity as DigitalClock; if (digitalClock != null) { if (ioData.ContainsKey("muted")) { digitalClock.muted = Convert.ToBoolean(ioData["muted"]); } if (ioData.ContainsKey("alarms") && ioData["alarms"] is List alarms) { foreach (Dictionary alarm in alarms) { if (alarm != null && alarm.ContainsKey("time") && alarm.ContainsKey("active")) { digitalClock.alarms.Add(new DigitalClock.Alarm(TimeSpan.Parse(alarm["time"].ToString()), Convert.ToBoolean(alarm["active"]))); } } } digitalClock.MarkDirty(); digitalClock.SendNetworkUpdate(); } if (inputs != null && inputs.Count > 0) { for (var index = 0; index < inputs.Count; index++) { var input = inputs[index] as Dictionary; object oldIdObject; if (!input.TryGetValue("connectedID", out oldIdObject)) continue; #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1712"); #endif var oldId = Convert.ToUInt64(oldIdObject); if (oldId != 0 && pasteData.EntityLookup.ContainsKey(oldId)) { if (ioEntity.inputs[index] == null) ioEntity.inputs[index] = new IOEntity.IOSlot(); var ioConnection = pasteData.EntityLookup[oldId]; if (ioConnection.ContainsKey("newId")) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1719"); #endif ioEntity.inputs[index].connectedTo.entityRef.uid = new NetworkableId(Convert.ToUInt64(ioConnection["newId"])); } } } } List outputs = null; if (ioData.ContainsKey("outputs")) outputs = ioData["outputs"] as List; if (outputs != null && outputs.Count > 0) { for (var index = 0; index < outputs.Count; index++) { var output = outputs[index] as Dictionary; #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1744"); #endif var oldId = Convert.ToUInt64(output["connectedID"]); if (oldId != 0 && pasteData.EntityLookup.ContainsKey(oldId)) { if (ioEntity.outputs[index] == null) ioEntity.outputs[index] = new IOEntity.IOSlot(); var ioConnection = pasteData.EntityLookup[oldId]; if( ioConnection.ContainsKey( "newId" ) ) { var ioOutput = ioEntity.outputs[index]; var ioEntity2 = ioConnection["entity"] as IOEntity; if (!ioEntity2.IsValid() || ioEntity2.IsDestroyed) continue; var connectedToSlot = Convert.ToInt32( output["connectedToSlot"] ); var ioInput = ioEntity2.inputs[connectedToSlot]; ioOutput.connectedTo = new IOEntity.IORef(); ioOutput.connectedTo.Set( ioEntity2 ); ioOutput.connectedToSlot = connectedToSlot; ioOutput.type = (IOEntity.IOType) Convert.ToInt32( output["type"] ); ioOutput.niceName = output["niceName"] as string; ioOutput.connectedTo.Init(); ioInput.connectedTo = new IOEntity.IORef(); ioInput.connectedTo.Set( ioEntity ); ioInput.connectedToSlot = index; ioInput.connectedTo.Init(); ioOutput.worldSpaceLineEndRotation = ioEntity2.transform.TransformDirection( ioInput.handleDirection ); ioOutput.originPosition = ioEntity.transform.position; ioOutput.originRotation = ioEntity.transform.rotation.eulerAngles; if( output.TryGetValue( "wireColour", out var wireColour ) ) { var color = (WireTool.WireColour) Convert.ToInt32( wireColour ); ioInput.wireColour = color; ioOutput.wireColour = color; } if (output.ContainsKey( "linePoints" ) && output["linePoints"] is List linePoints) { ioOutput.linePoints = new Vector3[linePoints.Count]; for( var i = 0; i < linePoints.Count; i++ ) { var linePoint = linePoints[i] as Dictionary; ioOutput.linePoints[i] = new Vector3( Convert.ToSingle( linePoint["x"] ), Convert.ToSingle( linePoint["y"] ), Convert.ToSingle( linePoint["z"] ) ); } } if (output.ContainsKey("slackLevels") && output["slackLevels"] is List slackLevels) { ioOutput.slackLevels = new float[slackLevels.Count]; for (var i = 0; i < slackLevels.Count; i++) { ioOutput.slackLevels[i] = Convert.ToSingle(slackLevels[i]); } } else { ioOutput.slackLevels = new float[ioOutput.linePoints.Count()]; for (var i = 0; i < ioOutput.slackLevels.Count(); i++) ioOutput.slackLevels[i] = 0f; } if (output.ContainsKey("lineAnchors") && output["lineAnchors"] is List lineAnchors) { ioOutput.lineAnchors = new IOEntity.LineAnchor[lineAnchors.Count]; for (var i = 0; i < lineAnchors.Count; i++) { var lineAnchor = lineAnchors[i] as Dictionary; var pos = (Dictionary)lineAnchor["position"]; if (pasteData.EntityLookup.TryGetValue(Convert.ToUInt64(lineAnchor["entityRefID"]), out var data)) { var door = data["entity"] as Door; if (door.IsValid()) { ioOutput.lineAnchors[i] = new IOEntity.LineAnchor { entityRef = new EntityRef(door.net.ID), position = new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]), Convert.ToSingle(pos["z"])), index = Convert.ToInt32(lineAnchor["index"]), boneName = lineAnchor["boneName"] as string }; } } } } ioEntity2.SendNetworkUpdate(); } } } } ioEntity.MarkDirty(); ioEntity.UpdateOutputs(); ioEntity.SendNetworkUpdate(); ioEntity.RefreshIndustrialPreventBuilding(); } private void SetItemSubEntity(PasteData pasteData, Item item, ulong oldId) { if (item != null && oldId != 0 && pasteData.EntityLookup.TryGetValue(oldId, out var data)) { if (data["entity"] is BaseEntity subEntity && subEntity.IsValid() && !subEntity.IsDestroyed && subEntity.net.ID.IsValid) { if (item.instanceData == null) item.instanceData = new ProtoBuf.Item.InstanceData(); item.instanceData.subEntity = subEntity.net.ID; } } } private void ExtractInventory(Dictionary data, ItemContainer inventory, CopyData copyData) { var itemlist = new List(); foreach (var item in inventory.itemList) { var itemdata = new Dictionary { { "condition", item.condition.ToString() }, { "id", item.info.itemid }, { "amount", item.amount }, { "skinid", item.skin }, { "position", item.position }, { "blueprintTarget", item.blueprintTarget }, { "dataInt", item.instanceData?.dataInt ?? 0 } }; if (item.instanceData != null) { if (item.instanceData.subEntity != default(NetworkableId)) { itemdata.Add("subEntity", item.instanceData.subEntity.Value); } // RF timed explosives if (item.instanceData.dataInt > 0 && item.info != null && item.info.Blueprint != null && item.info.Blueprint.workbenchLevelRequired == 3) { itemdata.Add("IsOn", item.IsOn()); } } if (!string.IsNullOrEmpty(item.name)) itemdata["name"] = item.name; if (!string.IsNullOrEmpty(item.text)) itemdata["text"] = item.text; var heldEnt = item.GetHeldEntity(); if (heldEnt != null) { var projectiles = heldEnt.GetComponent(); if (projectiles != null) { var magazine = projectiles.primaryMagazine; if (magazine != null) { itemdata.Add("magazine", new Dictionary { { magazine.ammoType.itemid.ToString(), magazine.contents } }); } } if (heldEnt.children != null && heldEnt.children.Count > 0) { var children = new List(); foreach (var child in heldEnt.children) { if (!child.IsValid()) continue; children.Add(EntityData(child, child.transform.position, child.transform.rotation.eulerAngles, copyData)); } if (children.Count > 0) itemdata["children"] = children; } } if (item?.contents?.itemList != null && item.contents.itemList.Count > 0) { var itemContents = new Dictionary(); ExtractInventory(itemContents, item.contents, copyData); if (itemContents.ContainsKey("items")) itemdata["items"] = itemContents["items"]; } itemlist.Add(itemdata); } data.Add("items", itemlist); } private void PopulateInventory(PasteData pasteData, Dictionary data, BaseEntity entity, ItemContainer inventory) { var items = new List(); if (data.ContainsKey("items")) items = data["items"] as List; foreach (var itemDef in items) { var item = itemDef as Dictionary; var itemid = Convert.ToInt32(item["id"]); var itemamount = Convert.ToInt32(item["amount"]); var itemskin = item.ContainsKey("skinid") ? ulong.Parse(item["skinid"].ToString()) : 0; var dataInt = item.ContainsKey("dataInt") ? Convert.ToInt32(item["dataInt"]) : 0; var growableEntity = entity as GrowableEntity; if (growableEntity != null) { if (data.ContainsKey("genes")) { var genesData = (int)data["genes"]; if (genesData > 0) { GrowableGeneEncoding.DecodeIntToGenes(genesData, growableEntity.Genes); } } if (data.ContainsKey("hasParent")) { var isParented = (bool)data["hasParent"]; if (isParented) { RaycastHit hitInfo; if (Physics.Raycast(growableEntity.transform.position, Vector3.down, out hitInfo, .5f, Rust.Layers.DefaultDeployVolumeCheck)) { var parentEntity = hitInfo.GetEntity(); if (parentEntity != null) { growableEntity.SetParent(parentEntity, true); } } } } } if (pasteData.IsItemReplace) itemid = GetItemId(itemid); var i = ItemManager.CreateByItemID(itemid, itemamount, itemskin); if (i != null) { if (item.ContainsKey("condition")) i.condition = Convert.ToSingle(item["condition"]); if (item.ContainsKey("text")) i.text = item["text"].ToString(); if (item.ContainsKey("name")) i.name = item["name"]?.ToString(); if (item.ContainsKey("blueprintTarget")) { var blueprintTarget = Convert.ToInt32(item["blueprintTarget"]); if (pasteData.IsItemReplace) blueprintTarget = GetItemId(blueprintTarget); i.blueprintTarget = blueprintTarget; } if (dataInt > 0) { i.instanceData = new ProtoBuf.Item.InstanceData() { ShouldPool = false, dataInt = dataInt }; } if (item.ContainsKey("IsOn")) { i.SetFlag(Item.Flag.IsOn, Convert.ToBoolean(item["IsOn"])); } if (item.ContainsKey("subEntity")) { // Needs to be processed after all of the children are spawned var oldId = Convert.ToUInt64(item["subEntity"]); if (oldId != 0) pasteData.ItemsWithSubEntity.Add(oldId, i); } if (item.ContainsKey("items")) { PopulateInventory(pasteData, item, null, i.contents); } var heldent = i.GetHeldEntity(); if (heldent != null) { if (item.ContainsKey("magazine")) { var projectiles = heldent.GetComponent(); if (projectiles != null) { var magazine = item["magazine"] as Dictionary; var ammotype = int.Parse(magazine.Keys.ToArray()[0]); var ammoamount = int.Parse(magazine[ammotype.ToString()].ToString()); if (pasteData.IsItemReplace) ammotype = GetItemId(ammotype); projectiles.primaryMagazine.ammoType = ItemManager.FindItemDefinition(ammotype); projectiles.primaryMagazine.contents = ammoamount; } } if (item.ContainsKey("children")) { PreLoadChildrenData(item); var children = item["children"] as List; if (children != null) { foreach (var child in children) { var childData = child as Dictionary; if (childData == null) continue; PasteEntity(childData, pasteData, heldent); } } } } var targetPos = -1; if (item.ContainsKey("position")) targetPos = Convert.ToInt32(item["position"]); var heldEntity = i.GetHeldEntity(); if (heldEntity != null && heldEntity is Detonator detonator) { detonator.frequency = dataInt; if ( detonator.IsOn() ) RFManager.AddBroadcaster(detonator.frequency, detonator); } i.position = targetPos; if (entity is WaterCatcher waterCatcher) { waterCatcher.Invoke(() => { if (waterCatcher != null && !waterCatcher.IsDestroyed) waterCatcher.inventory.AddItem(i.info, i.amount); }, 1f); } else { inventory.Insert(i); } } } } void ExtractTextures(Dictionary data, uint[] textureIDs, BaseEntity entity, FileStorage.Type type) { if (textureIDs != null && textureIDs.Length > 0) { var signData = new Dictionary(); if (entity is Signage sign && sign != null) signData.Add("locked", sign.IsLocked()); for (var num = 0; num < textureIDs.Length; num++) { var textureId = textureIDs[num]; if (textureId == 0) continue; var imageByte = FileStorage.server.Get(textureId, type, entity.net.ID); if (imageByte != null) { signData.Add($"texture{num}", Convert.ToBase64String(imageByte)); } } signData["amount"] = textureIDs.Length; data.Add("sign", signData); } } void ExtractCassette(Dictionary data, Cassette cassette) { if (cassette.IsValid()) { uint[] contentCRCs = cassette.GetContentCRCs; if (contentCRCs != null && contentCRCs.Length == 1) { var oggByte = FileStorage.server.Get(contentCRCs[0], FileStorage.Type.ogg, cassette.net.ID); if (oggByte != null) { data.Add("audio", Convert.ToBase64String(oggByte)); } } } } void PopulateCassette(Dictionary data, Cassette cassette) { if (data.ContainsKey("audio")) { var contentCRC = FileStorage.server.Store(Convert.FromBase64String(data["audio"].ToString()), FileStorage.Type.ogg, cassette.net.ID); cassette.SetAudioId(contentCRC, 0); } } void ExtractHeadData(Dictionary data, HeadData headData) { if (headData != null) { data.Add("currentTrophyData", new Dictionary { { "entitySource", headData.entitySource }, { "playerName", headData.playerName }, { "playerId", headData.playerId }, { "clothing", headData.clothing }, { "count", headData.count }, { "horseBreed", headData.horseBreed } }); } } void PopulateHeadData(Dictionary data, HeadData headData) { if (data.ContainsKey("currentTrophyData")) { var headDataData = (Dictionary)data["currentTrophyData"]; var clothing = Pool.Get>(); if (headDataData["clothing"] is List clothingData) { foreach (var clothingItem in clothingData) { clothing.Add(Convert.ToInt32(clothingItem)); } } if (clothing.Count == 0) Pool.FreeUnmanaged(ref clothing); headData.entitySource = Convert.ToUInt32(headDataData["entitySource"]); headData.playerName = headDataData["playerName"] as string; headData.playerId = Convert.ToUInt64(headDataData["playerId"]); headData.clothing = clothing; headData.count = Convert.ToUInt32(headDataData["count"]); headData.horseBreed = Convert.ToInt32(headDataData["horseBreed"]); } } private HashSet> PreLoadData(List entities, Vector3 startPos, float rotationCorrection, bool deployables, bool inventories, bool auth, bool vending) { var eulerRotation = new Vector3(0f, rotationCorrection, 0f); var quaternionRotation = Quaternion.Euler(eulerRotation * Mathf.Rad2Deg); var preloaddata = new HashSet>(); foreach (Dictionary entity in entities) { if (!deployables && !entity.ContainsKey("grade")) continue; var pos = (Dictionary)entity["pos"]; var rot = (Dictionary)entity["rot"]; entity.Add("position", quaternionRotation * new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]), Convert.ToSingle(pos["z"])) + startPos); entity.Add("rotation", Quaternion.Euler((eulerRotation + new Vector3(Convert.ToSingle(rot["x"]), Convert.ToSingle(rot["y"]), Convert.ToSingle(rot["z"]))) * Mathf.Rad2Deg)); if (!inventories && entity.ContainsKey("items")) entity["items"] = new List(); if (!vending && entity["prefabname"].ToString().Contains("vendingmachine")) entity.Remove("vendingmachine"); PreLoadChildrenData(entity); preloaddata.Add(entity); } return preloaddata; } private void PreLoadChildrenData(Dictionary entity) { if (entity.ContainsKey("children")) { var children = entity["children"] as List; if (children == null) return; // Set the (local) position and rotation of the children foreach (var child in children) { var childData = child as Dictionary; if (childData == null) continue; var childPos = (Dictionary)childData["pos"]; var childRot = (Dictionary)childData["rot"]; childData.Add("position", new Vector3(Convert.ToSingle(childPos["x"]), Convert.ToSingle(childPos["y"]), Convert.ToSingle(childPos["z"]))); childData.Add("rotation", Quaternion.Euler(new Vector3(Convert.ToSingle(childRot["x"]), Convert.ToSingle(childRot["y"]), Convert.ToSingle(childRot["z"])))); // Recursively process the child's children PreLoadChildrenData(childData); } } } private object TryCopy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection, string[] args, IPlayer player, Action callback) { bool saveShare = _config.Copy.Share, saveTree = _config.Copy.Tree, eachToEach = _config.Copy.EachToEach; var copyMechanics = CopyMechanics.Proximity; var radius = _config.Copy.Radius; for (var i = 0;; i += 2) { if (i >= args.Length) break; var valueIndex = i + 1; if (valueIndex >= args.Length) return Lang("SYNTAX_COPY"); var param = args[i].ToLower(); switch (param) { case "e": case "each": if (!bool.TryParse(args[valueIndex], out eachToEach)) return Lang("SYNTAX_BOOL", null, param); break; case "m": case "method": switch (args[valueIndex].ToLower()) { case "b": case "building": copyMechanics = CopyMechanics.Building; break; case "p": case "proximity": copyMechanics = CopyMechanics.Proximity; break; } break; case "r": case "radius": if (!float.TryParse(args[valueIndex], out radius)) return Lang("SYNTAX_RADIUS"); break; case "s": case "share": if (!bool.TryParse(args[valueIndex], out saveShare)) return Lang("SYNTAX_BOOL", null, param); break; case "t": case "tree": if (!bool.TryParse(args[valueIndex], out saveTree)) return Lang("SYNTAX_BOOL", null, param); break; default: return Lang("SYNTAX_COPY"); } } Copy(sourcePos, sourceRot, filename, rotationCorrection, copyMechanics, radius, saveTree, saveShare, eachToEach, player, callback); return true; } private bool GetSlot(BaseEntity parent, BaseEntity child, out BaseEntity.Slot? slot) { slot = null; for (int s = 0; s < (int)BaseEntity.Slot.Count; s++) { var slotEnum = (BaseEntity.Slot)s; if (parent.HasSlot( slotEnum ) && parent.GetSlot( slotEnum ) == child) { slot = slotEnum; return true; } } return false; } // private void TryCopySlots(BaseEntity ent, IDictionary housedata, bool saveShare) // { // foreach (var slot in _checkSlots) // { // if (!ent.HasSlot(slot)) // continue; // // var slotEntity = ent.GetSlot(slot); // // if (slotEntity == null) // continue; // // var codedata = new Dictionary // { // { "prefabname", slotEntity.PrefabName }, // { "flags", TryCopyFlags(ent) } // }; // // if (slotEntity.GetComponent()) // { // var codeLock = slotEntity.GetComponent(); // // codedata.Add("code", codeLock.code); // // if (saveShare) // codedata.Add("whitelistPlayers", codeLock.whitelistPlayers); // // if (codeLock.guestCode != null && codeLock.guestCode.Length == 4) // { // codedata.Add("guestCode", codeLock.guestCode); // // if (saveShare) // codedata.Add("guestPlayers", codeLock.guestPlayers); // } // } // else if (slotEntity.GetComponent()) // { // var keyLock = slotEntity.GetComponent(); // var code = keyLock.keyCode; // // if (keyLock.firstKeyCreated) // code |= 0x80; // // codedata.Add("ownerId", keyLock.OwnerID.ToString()); // codedata.Add("code", code.ToString()); // } // // var slotName = slot.ToString().ToLower(); // // housedata.Add(slotName, codedata); // } // } private Dictionary TryCopyFlags(BaseEntity entity) { var flags = new Dictionary(); foreach (BaseEntity.Flags flag in Enum.GetValues(typeof(BaseEntity.Flags))) { if (!_config.DataSaving || entity.HasFlag(flag)) flags.Add(flag.ToString(), entity.HasFlag(flag)); } return flags; } private ValueTuple TryPaste(Vector3 startPos, string filename, IPlayer player, float rotationCorrection, string[] args, bool autoHeight = true, Action callback = null, Action callbackSpawned = null) { var userId = player?.Id; var path = _subDirectory + filename; if (!Interface.Oxide.DataFileSystem.ExistsDatafile(path)) return new ValueTuple(Lang("FILE_NOT_EXISTS", userId), null); var data = Interface.Oxide.DataFileSystem.GetDatafile(path); if (data["default"] == null || data["entities"] == null) return new ValueTuple(Lang("FILE_BROKEN", userId), null); float heightAdj = 0f, blockCollision = 0f; bool auth = _config.Paste.Auth, inventories = _config.Paste.Inventories, deployables = _config.Paste.Deployables, vending = _config.Paste.VendingMachines, stability = _config.Paste.Stability, ownership = _config.Paste.EntityOwner, checkPlaced = true, enableSaving = true; for (var i = 0;; i += 2) { if (i >= args.Length) break; var valueIndex = i + 1; if (valueIndex >= args.Length) return new ValueTuple(Lang("SYNTAX_PASTE_OR_PASTEBACK", userId), null); var param = args[i].ToLower(); switch (param) { case "a": case "auth": if (!bool.TryParse(args[valueIndex], out auth)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "b": case "blockcollision": if (!float.TryParse(args[valueIndex], out blockCollision)) return new ValueTuple(Lang("SYNTAX_BLOCKCOLLISION", userId), null); break; case "d": case "deployables": if (!bool.TryParse(args[valueIndex], out deployables)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "h": case "height": if (!float.TryParse(args[valueIndex], out heightAdj)) return new ValueTuple(Lang("SYNTAX_HEIGHT", userId), null); break; case "i": case "inventories": if (!bool.TryParse(args[valueIndex], out inventories)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "s": case "stability": if (!bool.TryParse(args[valueIndex], out stability)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "v": case "vending": if (!bool.TryParse(args[valueIndex], out vending)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "o": case "entityowner": if (!bool.TryParse(args[valueIndex], out ownership)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "cp": case "checkplaced": if (!bool.TryParse(args[valueIndex], out checkPlaced)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "autoheight": if (!bool.TryParse(args[valueIndex], out autoHeight)) return new ValueTuple(Lang("SYNTAX_BOOL", userId, param), null); break; case "position": startPos = args[valueIndex].ToVector3(); break; case "rotation": if (!float.TryParse(args[valueIndex], out rotationCorrection)) return new ValueTuple(Lang("SYNTAX_FLOAT", userId, param), null); break; case "enablesaving": if (!bool.TryParse(args[valueIndex], out enableSaving)) return new(Lang("SYNTAX_BOOL", userId, param), null); break; default: return new ValueTuple(Lang("SYNTAX_PASTE_OR_PASTEBACK", userId), null); } } startPos.y += heightAdj; var preloadData = PreLoadData(data["entities"] as List, startPos, rotationCorrection, deployables, inventories, auth, vending); if (autoHeight) { var bestHeight = FindBestHeight(preloadData, startPos); if (bestHeight is string) return new ValueTuple(bestHeight, null); heightAdj += (float)bestHeight - startPos.y; foreach (var entity in preloadData) { var pos = (Vector3)entity["position"]; pos.y += heightAdj; entity["position"] = pos; } } if (blockCollision > 0f) { var collision = CheckCollision(preloadData, startPos, blockCollision); if (collision is string) return new ValueTuple(collision, null); } var protocol = new Dictionary(); if (data["protocol"] != null) protocol = data["protocol"] as Dictionary; var pasteData = Paste(preloadData, protocol, ownership, startPos, player, stability, rotationCorrection, autoHeight ? heightAdj : 0, auth, callback, callbackSpawned, filename, checkPlaced, enableSaving); return new ValueTuple(true, pasteData); } private void TryPasteLocks(BaseEntity entity, Dictionary data, PasteData pasteData) { if (entity.GetComponent()) { var code = (string)data["code"]; if (!string.IsNullOrEmpty(code)) { var codeLock = entity.GetComponent(); codeLock.code = code; codeLock.hasCode = true; if (pasteData.Auth && pasteData.BasePlayer != null) codeLock.whitelistPlayers.Add(pasteData.BasePlayer.userID); if (data.ContainsKey("whitelistPlayers")) { foreach (var userId in (List)data["whitelistPlayers"]) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 2206"); #endif codeLock.whitelistPlayers.Add(Convert.ToUInt64(userId)); } } if (data.ContainsKey("guestCode")) { var guestCode = (string)data["guestCode"]; codeLock.guestCode = guestCode; codeLock.hasGuestCode = true; if (data.ContainsKey("guestPlayers")) { foreach (var userId in (List)data["guestPlayers"]) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 2224"); #endif codeLock.guestPlayers.Add(Convert.ToUInt64(userId)); } } } codeLock.SetFlag(BaseEntity.Flags.Locked, true); } } else if (entity.GetComponent()) { var code = Convert.ToInt32(data["code"]); var keyLock = entity.GetComponent(); if (data.ContainsKey("firstKeyCreated")) { keyLock.keyCode = code; keyLock.firstKeyCreated = Convert.ToBoolean(data["firstKeyCreated"]); } else { if ((code & 0x80) != 0) { keyLock.keyCode = code & 0x7F; keyLock.firstKeyCreated = true; keyLock.SetFlag(BaseEntity.Flags.Locked, true); } } if (pasteData.Ownership && data.ContainsKey("ownerId")) { #if DEBUG Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 2249"); #endif keyLock.OwnerID = Convert.ToUInt64(data["ownerId"]); } } } private List TryPasteSlots(BaseEntity ent, Dictionary structure, PasteData pasteData) { var entitySlots = new List(); foreach (var slot in _checkSlots) { var slotName = slot.ToString().ToLower(); if (!ent.HasSlot(slot) || !structure.ContainsKey(slotName)) continue; var slotData = structure[slotName] as Dictionary; var slotEntity = GameManager.server.CreateEntity((string)slotData?["prefabname"], Vector3.zero); if (slotEntity == null) continue; slotEntity.gameObject.Identity(); slotEntity.SetParent(ent, slotName); slotEntity.OnDeployed(ent, null, _emptyItem); if (!pasteData.EnableSaving) { slotEntity.enableSaving = false; } slotEntity.Spawn(); ent.SetSlot(slot, slotEntity); entitySlots.Add(slotEntity); if (slotName == "lock" && slotData.ContainsKey("code")) TryPasteLocks(slotEntity, slotData, pasteData); pasteData.CallbackSpawned?.Invoke(ent); } return entitySlots; } private List DeSerializeConveyorFilter(string itemstring) { List itemFilters = new List(); try { foreach (string datapoint in Encoding.ASCII.GetString(Facepunch.Utility.Compression.Uncompress(Convert.FromBase64String(itemstring))).Split('\\')) { if (datapoint != null && !string.IsNullOrEmpty(datapoint)) { string[] info = datapoint.Split('/'); if (info.Length == 6) { IndustrialConveyor.ItemFilter item = new IndustrialConveyor.ItemFilter(); if (info[0] != "-1") { item.TargetItem = ItemManager.FindItemDefinition(Convert.ToInt32(info[0])); } item.MaxAmountInOutput = Convert.ToInt32(info[1]); item.BufferAmount = Convert.ToInt32(info[2]); item.MinAmountInInput = Convert.ToInt32(info[3]); if (info[4] != "-1") { item.TargetCategory = (ItemCategory)Convert.ToInt32(info[4]); } item.IsBlueprint = Convert.ToBoolean(info[5]); itemFilters.Add(item); } } } } catch { Puts("DeSerializeConveyorFilter Failed!"); } return itemFilters; } private string SerializeConveyorFilter(List filterItems, string itemstring = "") { if (filterItems?.Count > 0) { foreach (var item in filterItems) { itemstring += (item.TargetItem ? item.TargetItem.itemid : "-1") + "/" + item.MaxAmountInOutput + "/" + item.BufferAmount + "/" + item.MinAmountInInput + "/" + (item.TargetCategory.HasValue ? (int) item.TargetCategory.Value : "-1") + "/" + item.IsBlueprint + "\\"; } return Convert.ToBase64String(Facepunch.Utility.Compression.Compress(Encoding.ASCII.GetBytes(itemstring))); } return itemstring; } private List DeSerializeStarPattern(string stars) { List starlist = new List(); try { foreach (string datapoint in Encoding.ASCII.GetString(Facepunch.Utility.Compression.Uncompress(Convert.FromBase64String(stars))).Split('\\')) { if (datapoint != null && !string.IsNullOrEmpty(datapoint)) { string[] info = datapoint.Split('/'); if (info.Length == 6) { ProtoBuf.PatternFirework.Star star = new ProtoBuf.PatternFirework.Star(); star.position = new Vector2(Convert.ToSingle(info[0]), Convert.ToSingle(info[1])); star.color = new UnityEngine.Color(Convert.ToSingle(info[2]), Convert.ToSingle(info[3]), Convert.ToSingle(info[4]), Convert.ToSingle(info[5])); starlist.Add(star); } } } } catch { Puts("DeSerializeStarPattern Failed!"); } return starlist; } private string SerializeStarPattern(List stars, string starstring = "") { if (stars?.Count > 0) { foreach (var star in stars) { starstring += star.position.x + "/" + star.position.y + "/" + star.color.r + "/" + star.color.g + "/" + star.color.b + "/" + star.color.a + "\\"; } return Convert.ToBase64String(Facepunch.Utility.Compression.Compress(Encoding.ASCII.GetBytes(starstring))); } return starstring; } private object TryPasteBack(string filename, IPlayer player, string[] args) { var path = _subDirectory + filename; if (!Interface.Oxide.DataFileSystem.ExistsDatafile(path)) return Lang("FILE_NOT_EXISTS", player?.Id); var data = Interface.Oxide.DataFileSystem.GetDatafile(path); if (data["default"] == null || data["entities"] == null) return Lang("FILE_BROKEN", player?.Id); var defaultdata = data["default"] as Dictionary; var pos = defaultdata?["position"] as Dictionary; var rotationCorrection = Convert.ToSingle(defaultdata?["rotationdiff"]); var startPos = new Vector3(Convert.ToSingle(pos?["x"]), Convert.ToSingle(pos?["y"]), Convert.ToSingle(pos?["z"])); return TryPaste(startPos, filename, player, rotationCorrection, args, false).Item1; } private static bool HasGrade(BuildingBlock block, BuildingGrade.Enum grade, ulong skin) { foreach (var constructionGrade in block.blockDefinition.grades) { var baseGrade = constructionGrade.gradeBase; if (baseGrade.type == grade && baseGrade.skin == skin) return true; } return false; } [Command("copy")] private void CmdCopy(IPlayer player, string command, string[] args) { if (!HasAccess(player, _copyPermission)) { player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (args.Length < 1) { player.Reply(Lang("SYNTAX_COPY", player.Id)); return; } var basePlayer = player.Object as BasePlayer; var savename = args[0]; var success = TryCopyFromSteamId(basePlayer == null ? 0ul : basePlayer.userID, savename, args.Skip(1).ToArray()) as string; if (!string.IsNullOrEmpty(success)) player.Reply(success); } [Command("paste")] private void CmdPaste(IPlayer player, string command, string[] args) { if (!HasAccess(player, _pastePermission)) { player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (args.Length < 1) { player.Reply(Lang("SYNTAX_PASTE_OR_PASTEBACK", player.Id)); return; } var basePlayer = player.Object as BasePlayer; var success = TryPasteFromSteamId(basePlayer == null ? 0ul : basePlayer.userID, args[0], args.Skip(1).ToArray()) as string; if (!string.IsNullOrEmpty(success)) player.Reply(success); } [Command("copylist")] private void CmdList(IPlayer player, string command, string[] args) { if (!HasAccess(player, _listPermission)) { player.Reply(Lang("NO_ACCESS", player.Id)); return; } var files = Interface.Oxide.DataFileSystem.GetFiles(_subDirectory); var fileList = new List(); foreach (var file in files) { var strFileParts = file.Split('/'); var justfile = strFileParts[strFileParts.Length - 1].Replace(".json", ""); fileList.Add(justfile); } player.Reply(Lang("AVAILABLE_STRUCTURES", player.Id)); player.Reply(string.Join(", ", fileList.ToArray())); } [Command("pasteback")] private void CmdPasteBack(IPlayer player, string command, string[] args) { if (!HasAccess(player, _pastebackPermission)) { player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (args.Length < 1) { player.Reply(Lang("SYNTAX_PASTEBACK", player.Id)); return; } var success = TryPasteBack(args[0], player, args.Skip(1).ToArray()) as string; if (!string.IsNullOrEmpty(success)) player.Reply(success); } [Command("undo")] private void CmdUndo(IPlayer player, string command, string[] args) { if (!HasAccess(player, _undoPermission)) { player.Reply(Lang("NO_ACCESS", player.Id)); return; } if (!_lastPastes.ContainsKey(player.Id)) { player.Reply(Lang("NO_PASTED_STRUCTURE", player.Id)); return; } var entities = new HashSet(_lastPastes[player.Id].Pop().ToList()); UndoLoop(entities, player); } //Replace between old ItemID to new ItemID private static readonly Dictionary ReplaceItemId = new Dictionary { { -1461508848, 1545779598 }, { 2115555558, 588596902 }, { -533875561, 785728077 }, { 1621541165, 51984655 }, { -422893115, -1691396643 }, { 815896488, -1211166256 }, { 805088543, -1321651331 }, { 449771810, 605467368 }, { 1152393492, 1712070256 }, { 1578894260, -742865266 }, { 1436532208, 1638322904 }, { 542276424, -1841918730 }, { 1594947829, -17123659 }, { -1035059994, -1685290200 }, { 1818890814, -1036635990 }, { 1819281075, -727717969 }, { 1685058759, -1432674913 }, { 93029210, 1548091822 }, { -1565095136, 352130972 }, { -1775362679, 215754713 }, { -1775249157, 14241751 }, { -1280058093, -1023065463 }, { -420273765, -1234735557 }, { 563023711, -2139580305 }, { 790921853, -262590403 }, { -337261910, -2072273936 }, { 498312426, -1950721390 }, { 504904386, 1655650836 }, { -1221200300, -559599960 }, { 510887968, 15388698 }, { -814689390, 866889860 }, { 1024486167, 1382263453 }, { 2021568998, 609049394 }, { 97329, 1099314009 }, { 1046072789, -582782051 }, { 97409, -1273339005 }, { -1480119738, -1262185308 }, { 1611480185, 1931713481 }, { -1386464949, 1553078977 }, { 93832698, 1776460938 }, { -1063412582, -586342290 }, { -1887162396, -996920608 }, { -55660037, 1588298435 }, { 919780768, 1711033574 }, { -365801095, 1719978075 }, { 68998734, 613961768 }, { -853695669, 1443579727 }, { 271534758, 833533164 }, { -770311783, -180129657 }, { -1192532973, 1424075905 }, { -307490664, 1525520776 }, { 707427396, 602741290 }, { 707432758, -761829530 }, { -2079677721, 1783512007 }, { -1342405573, -1316706473 }, { -139769801, 1946219319 }, { -1043746011, -700591459 }, { 2080339268, 1655979682 }, { -171664558, -1941646328 }, { 1050986417, -1557377697 }, { -1693683664, 1789825282 }, { 523409530, 1121925526 }, { 1300054961, 634478325 }, { -2095387015, 1142993169 }, { 1428021640, 1104520648 }, { 94623429, 1534542921 }, { 1436001773, -1938052175 }, { 1711323399, 1973684065 }, { 1734319168, -1848736516 }, { -1658459025, -1440987069 }, { -726947205, -751151717 }, { -341443994, 363467698 }, { 1540879296, 2009734114 }, { 94756378, -858312878 }, { 3059095, 204391461 }, { 3059624, 1367190888 }, { 2045107609, -778875547 }, { 583366917, 998894949 }, { 2123300234, 1965232394 }, { 1983936587, -321733511 }, { 1257201758, -97956382 }, { -1144743963, 296519935 }, { -1144542967, -113413047 }, { -1144334585, -2022172587 }, { 1066729526, -1101924344 }, { -1598790097, 1390353317 }, { -933236257, 1221063409 }, { -1575287163, -1336109173 }, { -2104481870, -2067472972 }, { -1571725662, 1353298668 }, { 1456441506, 1729120840 }, { 1200628767, -1112793865 }, { -778796102, 1409529282 }, { 1526866730, 674734128 }, { 1925723260, -1519126340 }, { 1891056868, 1401987718 }, { 1295154089, -1878475007 }, { 498591726, 1248356124 }, { 1755466030, -592016202 }, { 726730162, 798638114 }, { -1034048911, -1018587433 }, { 252529905, 274502203 }, { 471582113, -1065444793 }, { -1138648591, 16333305 }, { 305916740, 649305914 }, { 305916742, 649305916 }, { 305916744, 649305918 }, { 1908328648, -1535621066 }, { -2078972355, 1668129151 }, { -533484654, 989925924 }, { 1571660245, 1569882109 }, { 1045869440, -1215753368 }, { 1985408483, 528668503 }, { 97513422, 304481038 }, { 1496470781, -196667575 }, { 1229879204, 952603248 }, { -1722829188, 936496778 }, { 1849912854, 1948067030 }, { -1266285051, 1413014235 }, { -1749787215, -1000573653 }, { 28178745, -946369541 }, { -505639592, -1999722522 }, { 1598149413, -1992717673 }, { -1779401418, -691113464 }, { -57285700, -335089230 }, { 98228420, 479143914 }, { 1422845239, 999690781 }, { 277631078, -1819763926 }, { 115739308, 1366282552 }, { -522149009, -690276911 }, { 3175989, -1899491405 }, { 718197703, -746030907 }, { 384204160, 1840822026 }, { -1308622549, 143803535 }, { -217113639, -2124352573 }, { -1580059655, -265876753 }, { -1832205789, 1070894649 }, { 305916741, 649305917 }, { 936777834, 3222790 }, { -1224598842, 200773292 }, { -1976561211, -1506397857 }, { -1406876421, 1675639563 }, { -1397343301, -23994173 }, { 1260209393, 850280505 }, { -1035315940, 1877339384 }, { -1381682752, 1714496074 }, { 696727039, -1022661119 }, { -2128719593, -803263829 }, { -1178289187, -1903165497 }, { 1351172108, 1181207482 }, { -450738836, -1539025626 }, { -966287254, -324675402 }, { 340009023, 671063303 }, { 124310981, -1478212975 }, { 1501403549, -2094954543 }, { 698310895, -1252059217 }, { 523855532, 1266491000 }, { 2045246801, -886280491 }, { 583506109, -237809779 }, { -148163128, 794356786 }, { -132588262, -1773144852 }, { -1666761111, 196700171 }, { -465236267, 442289265 }, { -1211618504, 1751045826 }, { 2133577942, -1982036270 }, { -1014825244, -682687162 }, { -991829475, 1536610005 }, { -642008142, -1709878924 }, { 661790782, 1272768630 }, { -1440143841, -1780802565 }, { 569119686, 1746956556 }, { 1404466285, -1102429027 }, { -1616887133, -48090175 }, { -1167640370, -1163532624 }, { -1284735799, 1242482355 }, { -1278649848, -1824943010 }, { 776005741, 1814288539 }, { 108061910, -316250604 }, { 255101535, -1663759755 }, { -51678842, 1658229558 }, { -789202811, 254522515 }, { 516382256, -132516482 }, { 50834473, 1381010055 }, { -975723312, 1159991980 }, { 1908195100, -850982208 }, { -1097452776, -110921842 }, { 146685185, -1469578201 }, { -1716193401, -1812555177 }, { 193190034, -2069578888 }, { 371156815, -852563019 }, { 3343606, -1966748496 }, { 825308669, -1137865085 }, { 830965940, -586784898 }, { 1662628660, -163828118 }, { 1662628661, -163828117 }, { 1662628662, -163828112 }, { -1832205788, 1070894648 }, { -1832205786, 1070894646 }, { 1625090418, 181590376 }, { -1269800768, -874975042 }, { 429648208, -1190096326 }, { -1832205787, 1070894647 }, { -1832205785, 1070894645 }, { 107868, 696029452 }, { 997973965, -2012470695 }, { -46188931, -702051347 }, { -46848560, -194953424 }, { -2066726403, -989755543 }, { -2043730634, 1873897110 }, { 1325935999, -1520560807 }, { -225234813, -78533081 }, { -202239044, -1509851560 }, { -322501005, 1422530437 }, { -1851058636, 1917703890 }, { -1828062867, -1162759543 }, { -1966381470, -1130350864 }, { 968732481, 1391703481 }, { 991728250, -242084766 }, { -253819519, 621915341 }, { -1714986849, 1827479659 }, { -1691991080, 813023040 }, { 179448791, -395377963 }, { 431617507, -1167031859 }, { 688032252, 69511070 }, { -1059362949, -4031221 }, { 1265861812, 1110385766 }, { 374890416, 317398316 }, { 1567404401, 1882709339 }, { -1057402571, 95950017 }, { -758925787, -1130709577 }, { -1411620422, 1052926200 }, { 88869913, -542577259 }, { -2094080303, 1318558775 }, { 843418712, -1962971928 }, { -1569356508, -1405508498 }, { -1569280852, 1478091698 }, { 449769971, 1953903201 }, { 590532217, -2097376851 }, { 3387378, 1414245162 }, { 1767561705, 1992974553 }, { 106433500, 237239288 }, { -1334615971, -1778159885 }, { -135651869, 1722154847 }, { -1595790889, 1850456855 }, { -459156023, -1695367501 }, { 106434956, -1779183908 }, { -578028723, -1302129395 }, { -586116979, 286193827 }, { -1379225193, -75944661 }, { -930579334, 649912614 }, { 548699316, 818877484 }, { 142147109, 1581210395 }, { 148953073, 1903654061 }, { 102672084, 980333378 }, { 640562379, -1651220691 }, { -1732316031, -1622660759 }, { -2130280721, 756517185 }, { -1725510067, -722241321 }, { 1974032895, -1673693549 }, { -225085592, -567909622 }, { 509654999, 1898094925 }, { 466113771, -1511285251 }, { 2033918259, 1373971859 }, { 2069925558, -1736356576 }, { -1026117678, 803222026 }, { 1987447227, -1861522751 }, { 540154065, -544317637 }, { 1939428458, 176787552 }, { -288010497, -2002277461 }, { -847065290, 1199391518 }, { 3506021, 963906841 }, { 649603450, 442886268 }, { 3506418, 1414245522 }, { 569935070, -1104881824 }, { 113284, -1985799200 }, { 1916127949, -277057363 }, { -1775234707, -1978999529 }, { -388967316, 1326180354 }, { 2007564590, -575483084 }, { -1705696613, 177226991 }, { 670655301, -253079493 }, { 1148128486, -1958316066 }, { -141135377, 567235583 }, { 109266897, -932201673 }, { -527558546, 2087678962 }, { -1745053053, -904863145 }, { 1223860752, 573926264 }, { -419069863, 1234880403 }, { -1617374968, -1994909036 }, { 2057749608, 1950721418 }, { 24576628, -2025184684 }, { -1659202509, 1608640313 }, { 2107229499, -1549739227 }, { 191795897, -765183617 }, { -1009492144, 795371088 }, { 2077983581, -1367281941 }, { 378365037, 352499047 }, { -529054135, -1199897169 }, { -529054134, -1199897172 }, { 486166145, -1023374709 }, { 1628490888, 23352662 }, { 1498516223, 1205607945 }, { -632459882, -1647846966 }, { -626812403, -845557339 }, { 385802761, -1370759135 }, { 2117976603, 121049755 }, { 1338515426, -996185386 }, { -1455694274, 98508942 }, { 1579245182, 2070189026 }, { -587434450, 1521286012 }, { -163742043, 1542290441 }, { -1224714193, -1832422579 }, { 644359987, 826309791 }, { -1962514734, -143132326 }, { -705305612, 1153652756 }, { -357728804, -1819233322 }, { -698499648, -1138208076 }, { 1213686767, -1850571427 }, { 386382445, -855748505 }, { 1859976884, 553887414 }, { 960793436, 996293980 }, { 1001265731, 2048317869 }, { 1253290621, -1754948969 }, { 470729623, -1293296287 }, { 1051155022, -369760990 }, { 865679437, -1878764039 }, { 927253046, -1039528932 }, { 109552593, 1796682209 }, { -2092529553, 1230323789 }, { 691633666, -363689972 }, { -2055888649, 1629293099 }, { 621575320, -41440462 }, { -2118132208, 1602646136 }, { -1127699509, 1540934679 }, { -685265909, -92759291 }, { 552706886, -1100422738 }, { 1835797460, -1021495308 }, { -892259869, 642482233 }, { -1623330855, -465682601 }, { -1616524891, 1668858301 }, { 789892804, 171931394 }, { -1289478934, -1583967946 }, { -892070738, -2099697608 }, { -891243783, -1581843485 }, { 889398893, -1157596551 }, { -1625468793, 1397052267 }, { 1293049486, 1975934948 }, { 1369769822, 559147458 }, { 586484018, 1079279582 }, { 110115790, 593465182 }, { 1490499512, 1523195708 }, { 3552619, 2019042823 }, { 1471284746, 73681876 }, { 456448245, -1758372725 }, { 110547964, 795236088 }, { 1588977225, -1667224349 }, { 918540912, -209869746 }, { -471874147, 1686524871 }, { 205978836, 1723747470 }, { -1044400758, -129230242 }, { -2073307447, -1331212963 }, { 435230680, 2106561762 }, { -864578046, 223891266 }, { 1660607208, 935692442 }, { 260214178, -1478445584 }, { -1847536522, 198438816 }, { -496055048, -967648160 }, { -1792066367, 99588025 }, { 562888306, -956706906 }, { -427925529, -1429456799 }, { 995306285, 1451568081 }, { -378017204, -1117626326 }, { 447918618, -148794216 }, { 313836902, 1516985844 }, { 1175970190, -796583652 }, { 525244071, -148229307 }, { -1021702157, -819720157 }, { -402507101, 671706427 }, { -1556671423, -1183726687 }, { 61936445, -1614955425 }, { 112903447, -1779180711 }, { 1817873886, -1100168350 }, { 1824679850, -132247350 }, { -1628526499, -1863559151 }, { 547302405, -119235651 }, { 1840561315, 2114754781 }, { -460592212, -1379835144 }, { 3655341, -151838493 }, { 1554697726, 418081930 }, { -1883959124, 832133926 }, { -481416622, 1524187186 }, { -481416621, -41896755 }, { -481416620, -1607980696 }, { -1151126752, 1058261682 }, { -1926458555, 794443127 } }; //Languages phrases private readonly Dictionary> _messages = new Dictionary> { { "FILE_NOT_EXISTS", new Dictionary { { "en", "File does not exist" }, { "ru", "Файл не существует" }, { "nl", "Bestand bestaat niet." } } }, { "FILE_BROKEN", new Dictionary { { "en", "Something went wrong during pasting because of a error in the file." }, { "ru", "Файл поврежден, вставка невозможна" }, { "nl", "Er is iets misgegaan tijdens het plakken door een beschadigd bestand." } } }, { "NO_ACCESS", new Dictionary { { "en", "You don't have the permissions to use this command" }, { "ru", "У вас нет прав доступа к данной команде" }, { "nl", "U heeft geen toestemming/permissie om dit commando te gebruiken." } } }, { "SYNTAX_PASTEBACK", new Dictionary { { "en", "Syntax: /pasteback \n" + "height XX - Adjust the height\n" + "vending - Information and sellings in vending machine\n" + "stability - Wether or not to disable stability\n" + "deployables - Wether or not to copy deployables\n" + "auth - Wether or not to copy lock and cupboard whitelists\n" + "position - Override position\n" + "rotation - Override rotation" }, { "ru", "Синтаксис: /pasteback <Название Объекта> <опция значение>\n" + "height XX - Высота от земли\n" + "vending - Информация и товары в торговом автомате" }, { "nl", "Syntax: /pasteback \n" + "height XX - Pas de hoogte aan \n" + "vending - Informatie en inventaris van \"vending machines\" kopiëren\n" + "stability - of de stabiliteit van het gebouw uitgezet moet worden\n" + "deployables - of de \"deployables\" gekopiërd moeten worden\n" + "auth - Of authorisatie op sloten en tool cupboards gekopiërd moet worden" } } }, { "SYNTAX_PASTE_OR_PASTEBACK", new Dictionary { { "en", "Syntax: /paste or /pasteback \n" + "height XX - Adjust the height\n" + "autoheight true/false - sets best height, carefull of the steep\n" + "blockcollision XX - blocks the entire paste if something the new building collides with something\n" + "deployables true/false - false to remove deployables\n" + "inventories true/false - false to ignore inventories\n" + "vending - Information and sellings in vending machine\n" + "stability - Wether or not to disable stability on the building\n" + "position - Override position\n" + "rotation - Override rotation" }, { "ru", "Синтаксис: /paste or /pasteback <Название Объекта> <опция значение>\n" + "height XX - Высота от земли\n" + "autoheight true/false - автоматически подобрать высоту от земли\n" + "blockcollision XX - блокировать вставку, если что-то этому мешает\n" + "deployables true/false - false для удаления предметов\n" + "inventories true/false - false для игнорирования копирования инвентаря\n" + "vending - Информация и товары в торговом автомате" }, { "nl", "Syntax: /paste of /pasteback \n" + "height XX - Pas de hoogte aan \n" + "autoheight true/false - probeert de optimale hoogte te vinden om gebouw te plaatsen. Werkt optimaal op vlakke grond.\n" + "vending true/false - Informatie en inventaris van \"vending machines\" kopiëren\n" + "stability - of de stabiliteit van het gebouw uitgezet moet worden\n" + "deployables - of de \"deployables\" gekopiërd moeten worden\n" + "auth - Of authorisatie op sloten en tool cupboards gekopiërd moet worden" } } }, { "PASTEBACK_SUCCESS", new Dictionary { { "en", "You've successfully placed back the structure" }, { "ru", "Постройка успешно вставлена на старое место" }, { "nl", "Het gebouw is succesvol teruggeplaatst." } } }, { "PASTE_SUCCESS", new Dictionary { { "en", "You've successfully pasted the structure" }, { "ru", "Постройка успешно вставлена" }, { "nl", "Het gebouw is succesvol geplaatst." } } }, { "SYNTAX_COPY", new Dictionary { { "en", "Syntax: /copy \n" + "radius XX (default 3) - The radius in which to search for the next object (performs this search from every other object)\n" + "method proximity/building (default proximity) - Building only copies objects which are part of the building, proximity copies everything (within the radius)\n" + "deployables true/false (saves deployables or not) - Wether to save deployables\n" + "inventories true/false (saves inventories or not) - Wether to save inventories of found objects with inventories." }, { "ru", "Синтаксис: /copy <Название Объекта> <опция значение>\n" + "radius XX (default 3)\n" + "method proximity/building (по умолчанию proximity)\n" + "deployables true/false (сохранять предметы или нет)\n" + "inventories true/false (сохранять инвентарь или нет)" }, { "nl", "Syntax: /copy \n" + "radius XX (standaard 3) - De radius waarin copy paste naar het volgende object zoekt\n" + "method proximity/building (standaard proximity) - Building kopieërd alleen objecten die bij het gebouw horen, proximity kopieërd alles wat gevonden is\n" + "deployables true/false (saves deployables or not) - Of de data van gevonden \"deployables\" opgeslagen moet worden\n" + "inventories true/false (saves inventories or not) - Of inventarissen van objecten (kisten, tool cupboards, etc) opgeslagen moet worden" } } }, { "NO_ENTITY_RAY", new Dictionary { { "en", "Couldn't ray something valid in front of you" }, { "ru", "Не удалось найти какой-либо объект перед вами" }, { "nl", "U kijkt niet naar een geschikt object om een kopie op te starten." } } }, { "COPY_SUCCESS", new Dictionary { { "en", "The structure was successfully copied as {0}" }, { "ru", "Постройка успешно скопирована под названием: {0}" }, { "nl", "Gebouw is succesvol gekopieërd" } } }, { "NO_PASTED_STRUCTURE", new Dictionary { { "en", "You must paste structure before undoing it" }, { "ru", "Вы должны вставить постройку перед тем, как отменить действие" }, { "nl", "U moet eerst een gebouw terugplaatsen alvorens deze ongedaan gemaakt kan worden (duhh)" } } }, { "UNDO_SUCCESS", new Dictionary { { "en", "You've successfully undid what you pasted" }, { "ru", "Вы успешно снесли вставленную постройку" }, { "nl", "Laatse geplaatste gebouw is succesvol ongedaan gemaakt." } } }, { "NOT_FOUND_PLAYER", new Dictionary { { "en", "Couldn't find the player" }, { "ru", "Не удалось найти игрока" }, { "nl", "Speler niet gevonden." } } }, { "SYNTAX_BOOL", new Dictionary { { "en", "Option {0} must be true/false" }, { "ru", "Опция {0} принимает значения true/false" }, { "nl", "Optie {0} moet true of false zijn" } } }, { "SYNTAX_FLOAT", new Dictionary { { "en", "Option {0} must be a decimal" }, { "ru", "Опция {0} принимает только числовые значения с точкой" }, { "nl", "Optie {0} moet een decimal zijn" } } }, { "SYNTAX_HEIGHT", new Dictionary { { "en", "Option height must be a number" }, { "ru", "Опция height принимает только числовые значения" }, { "nl", "De optie height accepteert alleen nummers" } } }, { "SYNTAX_BLOCKCOLLISION", new Dictionary { { "en", "Option blockcollision must be a number, 0 will deactivate the option" }, { "ru", "Опция blockcollision принимает только числовые значения, 0 позволяет отключить проверку" }, { "nl", "Optie blockcollision accepteert alleen nummers, 0 schakelt deze functionaliteit uit" } } }, { "SYNTAX_RADIUS", new Dictionary { { "en", "Option radius must be a number" }, { "ru", "Опция radius принимает только числовые значения" }, { "nl", "Optie height accepteert alleen nummers" } } }, { "BLOCKING_PASTE", new Dictionary { { "en", "Something is blocking the paste" }, { "ru", "Что-то препятствует вставке" }, { "nl", "Iets blokkeert het plaatsen van dit gebouw" } } }, { "AVAILABLE_STRUCTURES", new Dictionary { { "ru", "Доступные постройки:" }, { "en", "Available structures:" }, { "nl", "Beschikbare bestanden om te plaatsen zijn:" } } } }; public class CopyData { public IPlayer Player; public BasePlayer BasePlayer; public Stack CheckFrom = new Stack(); public HashSet HouseList = new HashSet(); public List RawData = new List(); public Vector3 SourcePos; public Vector3 SourceRot; public Action Callback; public string Filename; public int CurrentLayer; public float RotCor; public float Range; public bool SaveTree; public bool SaveShare; public CopyMechanics CopyMechanics; public bool EachToEach; public uint BuildingId = 0; #if DEBUG public Stopwatch Sw = new Stopwatch(); #endif } public class PasteData { public ICollection> Entities; public List PastedEntities = new List(); public string Filename; public Dictionary> EntityLookup = new Dictionary>(); public Dictionary ItemsWithSubEntity = new Dictionary(); public List FinalProcessingActions = new List(); public IPlayer Player; public BasePlayer BasePlayer; public List StabilityEntities = new List(); public List industrialStorageAdaptors = new List(); public Quaternion QuaternionRotation; public Action CallbackFinished; public Action CallbackSpawned; public bool Auth; public Vector3 StartPos; public float HeightAdj; public bool Stability; public bool IsItemReplace; public bool Ownership; public bool CheckPlaced = true; public bool EnableSaving = true; public bool Cancelled = false; public uint BuildingId = 0; public VersionNumber Version { get; set; } #if DEBUG public Stopwatch Sw = new Stopwatch(); #endif } private VersionNumber ParseVersionNumber(string versionString) { string[] array = versionString.Split(new char[1] { '.' }, StringSplitOptions.RemoveEmptyEntries); int major = int.Parse(array[0]); int minor = int.Parse(array[1]); int patch = int.Parse(array[2]); return new VersionNumber(major, minor, patch); } } }