· 5 years ago · Nov 24, 2020, 03:20 PM
1//If debug is defined it will add a stopwatch to the paste and copydata which can be used to profile copying and pasting.
2//#define DEBUG
3
4using System;
5using System.Collections.Generic;
6using System.ComponentModel;
7using System.Diagnostics;
8using System.Drawing;
9using System.Drawing.Imaging;
10using System.IO;
11using System.Linq;
12using Facepunch;
13using Newtonsoft.Json;
14using Oxide.Core;
15using ProtoBuf;
16using UnityEngine;
17using Graphics = System.Drawing.Graphics;
18
19/*
20 * CREDITS
21 *
22 * Orange - Saving ContainerIOEntity
23 * UIP88 - Turrets fix
24 * bsdinis - Wire fix
25 * nivex - Ownership option
26 *
27 */
28
29namespace Oxide.Plugins
30{
31 [Info("Copy Paste", "Reneb & MiRror & Misstake & misticos", "4.1.23")]
32 [Description("Copy and paste buildings to save them or move them")]
33
34 public class CopyPaste : RustPlugin
35 {
36 private int _copyLayer =
37 LayerMask.GetMask("Construction", "Prevent Building", "Construction Trigger", "Trigger", "Deployed",
38 "Default", "Ragdoll"),
39 _groundLayer = LayerMask.GetMask("Terrain", "Default"),
40 _rayCopy = LayerMask.GetMask("Construction", "Deployed", "Tree", "Resource", "Prevent Building"),
41 _rayPaste = LayerMask.GetMask("Construction", "Deployed", "Tree", "Terrain", "World", "Water",
42 "Prevent Building");
43
44 private string _copyPermission = "copypaste.copy",
45 _listPermission = "copypaste.list",
46 _pastePermission = "copypaste.paste",
47 _pastebackPermission = "copypaste.pasteback",
48 _undoPermission = "copypaste.undo",
49 _serverId = "Server",
50 _subDirectory = "copypaste/";
51
52 private Dictionary<string, Stack<List<BaseEntity>>> _lastPastes =
53 new Dictionary<string, Stack<List<BaseEntity>>>();
54
55 private Dictionary<string, SignSize> _signSizes = new Dictionary<string, SignSize>
56 {
57 //{"spinner.wheel.deployed", new SignSize(512, 512)},
58 {"sign.pictureframe.landscape", new SignSize(256, 128)},
59 {"sign.pictureframe.tall", new SignSize(128, 512)},
60 {"sign.pictureframe.portrait", new SignSize(128, 256)},
61 {"sign.pictureframe.xxl", new SignSize(1024, 512)},
62 {"sign.pictureframe.xl", new SignSize(512, 512)},
63 {"sign.small.wood", new SignSize(128, 64)},
64 {"sign.medium.wood", new SignSize(256, 128)},
65 {"sign.large.wood", new SignSize(256, 128)},
66 {"sign.huge.wood", new SignSize(512, 128)},
67 {"sign.hanging.banner.large", new SignSize(64, 256)},
68 {"sign.pole.banner.large", new SignSize(64, 256)},
69 {"sign.post.single", new SignSize(128, 64)},
70 {"sign.post.double", new SignSize(256, 256)},
71 {"sign.post.town", new SignSize(256, 128)},
72 {"sign.post.town.roof", new SignSize(256, 128)},
73 {"sign.hanging", new SignSize(128, 256)},
74 {"sign.hanging.ornate", new SignSize(256, 128)}
75 };
76
77 private List<BaseEntity.Slot> _checkSlots = new List<BaseEntity.Slot>
78 {
79 BaseEntity.Slot.Lock,
80 BaseEntity.Slot.UpperModifier,
81 BaseEntity.Slot.MiddleModifier,
82 BaseEntity.Slot.LowerModifier
83 };
84
85 public enum CopyMechanics
86 {
87 Building,
88 Proximity
89 }
90
91 private class SignSize
92 {
93 public int Width;
94 public int Height;
95
96 public SignSize(int width, int height)
97 {
98 Width = width;
99 Height = height;
100 }
101 }
102
103 //Config
104
105 private ConfigData _config;
106
107 private class ConfigData
108 {
109 [JsonProperty(PropertyName = "Copy Options")]
110 public CopyOptions Copy { get; set; }
111
112 [JsonProperty(PropertyName = "Paste Options")]
113 public PasteOptions Paste { get; set; }
114
115 [JsonProperty(PropertyName =
116 "Amount of entities to paste per batch. Use to tweak performance impact of pasting")]
117 [DefaultValue(15)]
118 public int PasteBatchSize = 15;
119
120 [JsonProperty(PropertyName =
121 "Amount of entities to copy per batch. Use to tweak performance impact of copying")]
122 [DefaultValue(100)]
123 public int CopyBatchSize = 100;
124
125 [JsonProperty(PropertyName =
126 "Amount of entities to undo per batch. Use to tweak performance impact of undoing")]
127 [DefaultValue(15)]
128 public int UndoBatchSize = 15;
129
130 [JsonProperty(PropertyName = "Enable data saving feature")]
131 [DefaultValue(true)]
132 public bool DataSaving = true;
133
134 public class CopyOptions
135 {
136 [JsonProperty(PropertyName = "Check radius from each entity (true/false)")]
137 [DefaultValue(true)]
138 public bool EachToEach { get; set; } = true;
139
140 [JsonProperty(PropertyName = "Share (true/false)")]
141 [DefaultValue(false)]
142 public bool Share { get; set; } = false;
143
144 [JsonProperty(PropertyName = "Tree (true/false)")]
145 [DefaultValue(false)]
146 public bool Tree { get; set; } = false;
147
148 [JsonProperty(PropertyName = "Default radius to look for entities from block")]
149 [DefaultValue(3.0f)]
150 public float Radius { get; set; } = 3.0f;
151 }
152
153 public class PasteOptions
154 {
155 [JsonProperty(PropertyName = "Auth (true/false)")]
156 [DefaultValue(false)]
157 public bool Auth { get; set; } = false;
158
159 [JsonProperty(PropertyName = "Deployables (true/false)")]
160 [DefaultValue(true)]
161 public bool Deployables { get; set; } = true;
162
163 [JsonProperty(PropertyName = "Inventories (true/false)")]
164 [DefaultValue(true)]
165 public bool Inventories { get; set; } = true;
166
167 [JsonProperty(PropertyName = "Vending Machines (true/false)")]
168 [DefaultValue(true)]
169 public bool VendingMachines { get; set; } = true;
170
171 [JsonProperty(PropertyName = "Stability (true/false)")]
172 [DefaultValue(true)]
173 public bool Stability { get; set; } = true;
174
175 [JsonProperty(PropertyName = "EntityOwner (true/false)")]
176 [DefaultValue(true)]
177 public bool EntityOwner { get; set; } = true;
178 }
179 }
180
181 private void LoadVariables()
182 {
183 Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate;
184
185 _config = Config.ReadObject<ConfigData>();
186
187 Config.WriteObject(_config, true);
188 }
189
190 protected override void LoadDefaultConfig()
191 {
192 var configData = new ConfigData
193 {
194 Copy = new ConfigData.CopyOptions(),
195 Paste = new ConfigData.PasteOptions()
196 };
197
198 Config.WriteObject(configData, true);
199 }
200
201 //Hooks
202
203 private void Init()
204 {
205 permission.RegisterPermission(_copyPermission, this);
206 permission.RegisterPermission(_listPermission, this);
207 permission.RegisterPermission(_pastePermission, this);
208 permission.RegisterPermission(_pastebackPermission, this);
209 permission.RegisterPermission(_undoPermission, this);
210
211 var compiledLangs = new Dictionary<string, Dictionary<string, string>>();
212
213 foreach (var line in _messages)
214 {
215 foreach (var translate in line.Value)
216 {
217 if (!compiledLangs.ContainsKey(translate.Key))
218 compiledLangs[translate.Key] = new Dictionary<string, string>();
219
220 compiledLangs[translate.Key][line.Key] = translate.Value;
221 }
222 }
223
224 foreach (var cLangs in compiledLangs)
225 {
226 lang.RegisterMessages(cLangs.Value, this, cLangs.Key);
227 }
228 }
229
230 private void OnServerInitialized()
231 {
232 LoadVariables();
233
234 Vis.colBuffer = new Collider[8192 * 16];
235
236 JsonConvert.DefaultSettings = () => new JsonSerializerSettings
237 {
238 Formatting = Formatting.Indented,
239 ReferenceLoopHandling = ReferenceLoopHandling.Ignore
240 };
241 }
242
243 #region API
244
245 private object TryCopyFromSteamId(ulong userId, string filename, string[] args, Action callback = null)
246 {
247 var player = BasePlayer.FindByID(userId);
248
249 if (player == null)
250 return Lang("NOT_FOUND_PLAYER", userId.ToString());
251
252 RaycastHit hit;
253
254 if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayCopy))
255 return Lang("NO_ENTITY_RAY", player.UserIDString);
256
257 return TryCopy(hit.point, hit.GetEntity().GetNetworkRotation().eulerAngles, filename,
258 DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player, callback);
259 }
260
261 private object TryPasteFromSteamId(ulong userId, string filename, string[] args, Action callback = null)
262 {
263 var player = BasePlayer.FindByID(userId);
264
265 if (player == null)
266 return Lang("NOT_FOUND_PLAYER", player.UserIDString);
267
268 RaycastHit hit;
269
270 if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayPaste))
271 return Lang("NO_ENTITY_RAY", player.UserIDString);
272
273 return TryPaste(hit.point, filename, player, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y),
274 args, callback: callback);
275 }
276
277 private object TryPasteFromVector3(Vector3 pos, float rotationCorrection, string filename, string[] args,
278 Action callback = null)
279 {
280 return TryPaste(pos, filename, null, rotationCorrection, args, callback: callback);
281 }
282
283 #endregion
284
285 //Other methods
286
287 private object CheckCollision(HashSet<Dictionary<string, object>> entities, Vector3 startPos, float radius)
288 {
289 foreach (var entityobj in entities)
290 {
291 if (Physics.CheckSphere((Vector3) entityobj["position"], radius, _copyLayer))
292 return Lang("BLOCKING_PASTE");
293 }
294
295 return true;
296 }
297
298 private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot)
299 {
300 const float maxDiff = 0.01f;
301
302 var ents = new List<BaseEntity>();
303 Vis.Entities(pos, maxDiff, ents);
304
305 foreach (var ent in ents)
306 {
307 if (ent.PrefabName != prefabname)
308 continue;
309
310 if (Vector3.Distance(ent.transform.position, pos) > maxDiff)
311 {
312 continue;
313 }
314
315 if (Vector3.Distance(ent.transform.rotation.eulerAngles, rot.eulerAngles) > maxDiff)
316 {
317 continue;
318 }
319
320 return true;
321 }
322
323 return false;
324 }
325
326 private object CmdPasteBack(BasePlayer player, string[] args)
327 {
328 var userIdString = (player == null) ? _serverId : player.UserIDString;
329
330 if (args.Length < 1)
331 return Lang("SYNTAX_PASTEBACK", userIdString);
332
333 var success = TryPasteBack(args[0], player, args.Skip(1).ToArray());
334
335 if (success is string)
336 return (string) success;
337
338 return true;
339 }
340
341 private object CmdUndo(string userIdString, string[] args)
342 {
343 var player = BasePlayer.Find(userIdString);
344 if (!_lastPastes.ContainsKey(userIdString))
345 return Lang("NO_PASTED_STRUCTURE", userIdString);
346
347 var entities = new HashSet<BaseEntity>(_lastPastes[userIdString].Pop().ToList());
348
349 UndoLoop(entities, player);
350
351 return true;
352 }
353
354 private void UndoLoop(HashSet<BaseEntity> entities, BasePlayer player, int count = 0)
355 {
356 foreach (var storageContainer in entities.OfType<StorageContainer>().Where(x => !x.IsDestroyed))
357 {
358 storageContainer.Kill();
359 }
360
361 // 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.
362 entities
363 .Take(_config.UndoBatchSize)
364 .ToList()
365 .ForEach(p =>
366 {
367 entities.Remove(p);
368
369 // Cleanup the hotspot beloning to the node.
370 var ore = p as OreResourceEntity;
371 if (ore != null)
372 {
373 ore.CleanupBonus();
374 }
375
376 if (p != null && !p.IsDestroyed)
377 p.Kill();
378 });
379
380 // If it gets stuck in infinite loop break the loop.
381 if (count != 0 && entities.Count != 0 && entities.Count == count)
382 {
383 if (player != null)
384 SendReply(player, "Undo cancelled because of infinite loop.");
385 else
386 Puts("Undo cancelled because of infinite loop.");
387 return;
388 }
389
390 if (entities.Count > 0)
391 NextTick(() => UndoLoop(entities, player, entities.Count));
392 else
393 {
394 if (player != null)
395 SendReply(player, Lang("UNDO_SUCCESS", player.UserIDString));
396 else
397 Puts(Lang("UNDO_SUCCESS"));
398
399 if (_lastPastes[player?.UserIDString ?? _serverId].Count == 0)
400 _lastPastes.Remove(player?.UserIDString ?? _serverId);
401 }
402 }
403
404 private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection,
405 CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, BasePlayer player,
406 Action callback)
407 {
408 var currentLayer = _copyLayer;
409
410 if (saveTree)
411 currentLayer |= LayerMask.GetMask("Tree");
412
413 var copyData = new CopyData
414 {
415 FileName = filename,
416 CurrentLayer = currentLayer,
417 RotCor = rotationCorrection,
418 Range = range,
419 SaveShare = saveShare,
420 SaveTree = saveTree,
421 CopyMechanics = copyMechanics,
422 EachToEach = eachToEach,
423 SourcePos = sourcePos,
424 SourceRot = sourceRot,
425 Player = player,
426 Callback = callback
427 };
428
429 copyData.CheckFrom.Push(sourcePos);
430
431 NextTick(() => CopyLoop(copyData));
432 ;
433 }
434
435 // Main loop for copy, will fetch all the data needed. Is called every tick untill copy is done (can't find any entities)
436 private void CopyLoop(CopyData copyData)
437 {
438 var checkFrom = copyData.CheckFrom;
439 var houseList = copyData.HouseList;
440 var buildingId = copyData.BuildingId;
441 var copyMechanics = copyData.CopyMechanics;
442 var batchSize = checkFrom.Count < _config.CopyBatchSize ? checkFrom.Count : _config.CopyBatchSize;
443
444 for (var i = 0; i < batchSize; i++)
445 {
446 if (checkFrom.Count == 0)
447 break;
448
449 var list = Pool.GetList<BaseEntity>();
450 Vis.Entities(checkFrom.Pop(), copyData.Range, list, copyData.CurrentLayer);
451
452 foreach (var entity in list)
453 {
454 if (!houseList.Add(entity))
455 continue;
456
457 if (copyMechanics == CopyMechanics.Building)
458 {
459 var buildingBlock = entity.GetComponentInParent<BuildingBlock>();
460
461 if (buildingBlock != null)
462 {
463 if (buildingId == 0)
464 buildingId = buildingBlock.buildingID;
465
466 if (buildingId != buildingBlock.buildingID)
467 continue;
468 }
469 }
470
471 if (copyData.EachToEach)
472 checkFrom.Push(entity.transform.position);
473 if (entity.GetComponent<BaseLock>() != null)
474 continue;
475 copyData.RawData.Add(EntityData(entity, entity.transform.position,
476 entity.transform.rotation.eulerAngles / 57.29578f, copyData));
477 }
478
479 copyData.BuildingId = buildingId;
480 }
481
482 if (checkFrom.Count > 0)
483 {
484 NextTick(() => CopyLoop(copyData));
485 }
486 else
487 {
488 var path = _subDirectory + copyData.FileName;
489 var datafile = Interface.Oxide.DataFileSystem.GetDatafile(path);
490
491 datafile.Clear();
492
493 var sourcePos = copyData.SourcePos;
494
495 datafile["default"] = new Dictionary<string, object>
496 {
497 {
498 "position", new Dictionary<string, object>
499 {
500 {"x", sourcePos.x.ToString()},
501 {"y", sourcePos.y.ToString()},
502 {"z", sourcePos.z.ToString()}
503 }
504 },
505 {"rotationy", copyData.SourceRot.y.ToString()},
506 {"rotationdiff", copyData.RotCor.ToString()}
507 };
508
509 datafile["entities"] = copyData.RawData;
510 datafile["protocol"] = new Dictionary<string, object>
511 {
512 {"items", 2},
513 {"version", Version}
514 };
515
516 Interface.Oxide.DataFileSystem.SaveDatafile(path);
517
518 SendReply(copyData.Player, Lang("COPY_SUCCESS", copyData.Player.UserIDString, copyData.FileName));
519
520 copyData.Callback?.Invoke();
521
522 Interface.CallHook("OnCopyFinished", copyData.RawData);
523 }
524 }
525
526 private float DegreeToRadian(float angle)
527 {
528 return (float) (Math.PI * angle / 180.0f);
529 }
530
531 private Dictionary<string, object> EntityData(BaseEntity entity, Vector3 entPos, Vector3 entRot,
532 CopyData copyData)
533 {
534 var normalizedPos = NormalizePosition(copyData.SourcePos, entPos, copyData.RotCor);
535
536 entRot.y -= copyData.RotCor;
537
538 var data = new Dictionary<string, object>
539 {
540 {"prefabname", entity.PrefabName},
541 {"skinid", entity.skinID},
542 {"flags", TryCopyFlags(entity)},
543 {
544 "pos", new Dictionary<string, object>
545 {
546 {"x", normalizedPos.x.ToString()},
547 {"y", normalizedPos.y.ToString()},
548 {"z", normalizedPos.z.ToString()}
549 }
550 },
551 {
552 "rot", new Dictionary<string, object>
553 {
554 {"x", entRot.x.ToString()},
555 {"y", entRot.y.ToString()},
556 {"z", entRot.z.ToString()}
557 }
558 },
559 {"ownerid", entity.OwnerID}
560 };
561
562 TryCopySlots(entity, data, copyData.SaveShare);
563
564 var buildingblock = entity as BuildingBlock;
565
566 if (buildingblock != null)
567 {
568 data.Add("grade", buildingblock.grade);
569 }
570
571 var box = entity as StorageContainer;
572 if (box?.inventory != null)
573 {
574 var itemlist = new List<object>();
575
576 foreach (var item in box.inventory.itemList)
577 {
578 var itemdata = new Dictionary<string, object>
579 {
580 {"condition", item.condition.ToString()},
581 {"id", item.info.itemid},
582 {"amount", item.amount},
583 {"skinid", item.skin},
584 {"position", item.position},
585 {"blueprintTarget", item.blueprintTarget}
586 };
587
588 if (!string.IsNullOrEmpty(item.text))
589 itemdata["text"] = item.text;
590
591 var heldEnt = item.GetHeldEntity();
592
593 if (heldEnt != null)
594 {
595 var projectiles = heldEnt.GetComponent<BaseProjectile>();
596
597 if (projectiles != null)
598 {
599 var magazine = projectiles.primaryMagazine;
600
601 if (magazine != null)
602 {
603 itemdata.Add("magazine", new Dictionary<string, object>
604 {
605 {magazine.ammoType.itemid.ToString(), magazine.contents}
606 });
607 }
608 }
609 }
610
611 if (item?.contents?.itemList != null)
612 {
613 var contents = new List<object>();
614
615 foreach (var itemContains in item.contents.itemList)
616 {
617 contents.Add(new Dictionary<string, object>
618 {
619 {"id", itemContains.info.itemid},
620 {"amount", itemContains.amount}
621 });
622 }
623
624 itemdata["items"] = contents;
625 }
626
627 itemlist.Add(itemdata);
628 }
629
630 data.Add("items", itemlist);
631 }
632
633 var box2 = entity as ContainerIOEntity;
634 if (box2 != null)
635 {
636 var itemlist = new List<object>();
637
638 foreach (var item in box2.inventory.itemList)
639 {
640 var itemdata = new Dictionary<string, object>
641 {
642 {"condition", item.condition.ToString()},
643 {"id", item.info.itemid},
644 {"amount", item.amount},
645 {"skinid", item.skin},
646 {"position", item.position},
647 {"blueprintTarget", item.blueprintTarget}
648 };
649
650 if (!string.IsNullOrEmpty(item.text))
651 itemdata["text"] = item.text;
652
653 var heldEnt = item.GetHeldEntity();
654
655 if (heldEnt != null)
656 {
657 var projectiles = heldEnt.GetComponent<BaseProjectile>();
658
659 if (projectiles != null)
660 {
661 var magazine = projectiles.primaryMagazine;
662
663 if (magazine != null)
664 {
665 itemdata.Add("magazine", new Dictionary<string, object>
666 {
667 {magazine.ammoType.itemid.ToString(), magazine.contents}
668 });
669 }
670 }
671 }
672
673 if (item?.contents?.itemList != null)
674 {
675 var contents = new List<object>();
676
677 foreach (var itemContains in item.contents.itemList)
678 {
679 contents.Add(new Dictionary<string, object>
680 {
681 {"id", itemContains.info.itemid},
682 {"amount", itemContains.amount}
683 });
684 }
685
686 itemdata["items"] = contents;
687 }
688
689 itemlist.Add(itemdata);
690 }
691
692 data.Add("items", itemlist);
693 }
694
695 var sign = entity as Signage;
696 if (sign != null)
697 {
698 var imageByte = FileStorage.server.Get(sign.textureID, FileStorage.Type.png, sign.net.ID);
699
700 data.Add("sign", new Dictionary<string, object>
701 {
702 {"locked", sign.IsLocked()}
703 });
704
705 if (sign.textureID > 0 && imageByte != null)
706 ((Dictionary<string, object>) data["sign"]).Add("texture", Convert.ToBase64String(imageByte));
707 }
708
709 if (copyData.SaveShare)
710 {
711 var sleepingBag = entity as SleepingBag;
712
713 if (sleepingBag != null)
714 {
715 data.Add("sleepingbag", new Dictionary<string, object>
716 {
717 {"niceName", sleepingBag.niceName},
718 {"deployerUserID", sleepingBag.deployerUserID},
719 {"isPublic", sleepingBag.IsPublic()}
720 });
721 }
722
723 var cupboard = entity as BuildingPrivlidge;
724
725 if (cupboard != null)
726 {
727 data.Add("cupboard", new Dictionary<string, object>
728 {
729 {"authorizedPlayers", cupboard.authorizedPlayers.Select(y => y.userid).ToList()}
730 });
731 }
732
733 var autoTurret = entity as AutoTurret;
734
735 if (autoTurret != null)
736 {
737 data.Add("autoturret", new Dictionary<string, object>
738 {
739 {"authorizedPlayers", autoTurret.authorizedPlayers.Select(p => p.userid).ToList()}
740 });
741 }
742 }
743
744 var cctvRC = entity as CCTV_RC;
745
746 if (cctvRC != null)
747 {
748 data.Add("cctv", new Dictionary<string, object>
749 {
750 {"yaw", cctvRC.yawAmount},
751 {"pitch", cctvRC.pitchAmount},
752 {"rcIdentifier", cctvRC.rcIdentifier}
753 });
754 }
755
756 var vendingMachine = entity as VendingMachine;
757
758 if (vendingMachine != null)
759 {
760 var sellOrders = new List<object>();
761
762 foreach (var vendItem in vendingMachine.sellOrders.sellOrders)
763 {
764 sellOrders.Add(new Dictionary<string, object>
765 {
766 {"itemToSellID", vendItem.itemToSellID},
767 {"itemToSellAmount", vendItem.itemToSellAmount},
768 {"currencyID", vendItem.currencyID},
769 {"currencyAmountPerItem", vendItem.currencyAmountPerItem},
770 {"inStock", vendItem.inStock},
771 {"currencyIsBP", vendItem.currencyIsBP},
772 {"itemToSellIsBP", vendItem.itemToSellIsBP}
773 });
774 }
775
776 data.Add("vendingmachine", new Dictionary<string, object>
777 {
778 {"shopName", vendingMachine.shopName},
779 {"isBroadcasting", vendingMachine.IsBroadcasting()},
780 {"sellOrders", sellOrders}
781 });
782 }
783
784 var ioEntity = entity as IOEntity;
785
786 if (ioEntity != null)
787 {
788 var ioData = new Dictionary<string, object>();
789 var inputs = ioEntity.inputs.Select(input => new Dictionary<string, object>
790 {
791 {"connectedID", input.connectedTo.entityRef.uid},
792 {"connectedToSlot", input.connectedToSlot},
793 {"niceName", input.niceName},
794 {"type", (int) input.type}
795 })
796 .Cast<object>()
797 .ToList();
798
799 ioData.Add("inputs", inputs);
800
801 var outputs = new List<object>();
802 foreach (var output in ioEntity.outputs)
803 {
804 var ioConnection = new Dictionary<string, object>
805 {
806 {"connectedID", output.connectedTo.entityRef.uid},
807 {"connectedToSlot", output.connectedToSlot},
808 {"niceName", output.niceName},
809 {"type", (int) output.type},
810 {"linePoints", output.linePoints?.ToList() ?? new List<Vector3>()}
811 };
812
813 outputs.Add(ioConnection);
814 }
815
816 ioData.Add("outputs", outputs);
817 ioData.Add("oldID", ioEntity.net.ID);
818 var electricalBranch = ioEntity as ElectricalBranch;
819 if (electricalBranch != null)
820 {
821 ioData.Add("branchAmount", electricalBranch.branchAmount);
822 }
823
824 var counter = ioEntity.GetComponent<PowerCounter>();
825 if (counter != null)
826 {
827 ioData.Add("targetNumber", counter.GetTarget());
828 }
829
830 var timerSwitch = ioEntity as TimerSwitch;
831 if (timerSwitch != null)
832 {
833 ioData.Add("timerLength", timerSwitch.timerLength);
834 }
835
836 var rfBroadcaster = ioEntity as IRFObject;
837 if (rfBroadcaster != null)
838 {
839 ioData.Add("frequency", rfBroadcaster.GetFrequency());
840 }
841
842 data.Add("IOEntity", ioData);
843 }
844
845 return data;
846 }
847
848 private object FindBestHeight(HashSet<Dictionary<string, object>> entities, Vector3 startPos)
849 {
850 var maxHeight = 0f;
851
852 foreach (var entity in entities)
853 {
854 if (((string) entity["prefabname"]).Contains("/foundation/"))
855 {
856 var foundHeight = GetGround((Vector3) entity["position"]);
857
858 if (foundHeight != null)
859 {
860 var height = (Vector3) foundHeight;
861
862 if (height.y > maxHeight)
863 maxHeight = height.y;
864 }
865 }
866 }
867
868 maxHeight += 1f;
869
870 return maxHeight;
871 }
872
873 private bool FindRayEntity(Vector3 sourcePos, Vector3 sourceDir, out Vector3 point, out BaseEntity entity,
874 int rayLayer)
875 {
876 RaycastHit hitinfo;
877 entity = null;
878 point = Vector3.zero;
879
880 if (!Physics.Raycast(sourcePos, sourceDir, out hitinfo, 1000f, rayLayer))
881 return false;
882
883 entity = hitinfo.GetEntity();
884 point = hitinfo.point;
885
886 return true;
887 }
888
889 private void FixSignage(Signage sign, byte[] imageBytes)
890 {
891 if (!_signSizes.ContainsKey(sign.ShortPrefabName))
892 return;
893
894 var resizedImage = ImageResize(imageBytes, _signSizes[sign.ShortPrefabName].Width,
895 _signSizes[sign.ShortPrefabName].Height);
896
897 sign.textureID = FileStorage.server.Store(resizedImage, FileStorage.Type.png, sign.net.ID);
898 }
899
900 private object GetGround(Vector3 pos)
901 {
902 RaycastHit hitInfo;
903 pos += new Vector3(0, 100, 0);
904
905 if (Physics.Raycast(pos, Vector3.down, out hitInfo, 200, _groundLayer))
906 return hitInfo.point;
907
908 return null;
909 }
910
911 private int GetItemId(int itemId)
912 {
913 if (ReplaceItemId.ContainsKey(itemId))
914 return ReplaceItemId[itemId];
915
916 return itemId;
917 }
918
919 private bool HasAccess(BasePlayer player, string permName)
920 {
921 return player.IsAdmin || permission.UserHasPermission(player.UserIDString, permName);
922 }
923
924 private byte[] ImageResize(byte[] imageBytes, int width, int height)
925 {
926 Bitmap resizedImage = new Bitmap(width, height),
927 sourceImage = new Bitmap(new MemoryStream(imageBytes));
928
929 Graphics.FromImage(resizedImage).DrawImage(sourceImage, new Rectangle(0, 0, width, height),
930 new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel);
931
932 var ms = new MemoryStream();
933 resizedImage.Save(ms, ImageFormat.Png);
934
935 return ms.ToArray();
936 }
937
938 private string Lang(string key, string userId = null, params object[] args) =>
939 string.Format(lang.GetMessage(key, this, userId), args);
940
941 private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float diffRot)
942 {
943 var transformedPos = currentPos - initialPos;
944 var newX = (transformedPos.x * (float) Math.Cos(-diffRot)) +
945 (transformedPos.z * (float) Math.Sin(-diffRot));
946 var newZ = (transformedPos.z * (float) Math.Cos(-diffRot)) -
947 (transformedPos.x * (float) Math.Sin(-diffRot));
948
949 transformedPos.x = newX;
950 transformedPos.z = newZ;
951
952 return transformedPos;
953 }
954
955 private void Paste(ICollection<Dictionary<string, object>> entities, Dictionary<string, object> protocol,
956 bool ownership, Vector3 startPos, BasePlayer player, bool stability, float rotationCorrection,
957 float heightAdj, bool auth, Action callback)
958 {
959
960 var ioEntities = new Dictionary<uint, Dictionary<string, object>>();
961 uint buildingId = 0;
962
963 //Settings
964
965 var isItemReplace = !protocol.ContainsKey("items");
966
967 var eulerRotation = new Vector3(0f, rotationCorrection * 57.2958f, 0f);
968 var quaternionRotation = Quaternion.Euler(eulerRotation);
969
970 var pasteData = new PasteData
971 {
972 HeightAdj = heightAdj,
973 IsItemReplace = isItemReplace,
974 Entities = entities,
975 Player = player,
976 QuaternionRotation = quaternionRotation,
977 StartPos = startPos,
978 Stability = stability,
979 Auth = auth,
980 Ownership = ownership,
981 Callback = callback
982 };
983
984 NextTick(() => PasteLoop(pasteData));
985 }
986
987 private void PasteLoop(PasteData pasteData)
988 {
989 var entities = pasteData.Entities;
990 var todo = entities.Take(_config.PasteBatchSize).ToArray();
991 var player = pasteData.Player;
992
993 foreach (var data in todo)
994 {
995 entities.Remove(data);
996 var prefabname = (string) data["prefabname"];
997 var skinid = ulong.Parse(data["skinid"].ToString());
998 var pos = (Vector3) data["position"];
999 var rot = (Quaternion) data["rotation"];
1000
1001 var ownerId = player?.userID ?? 0;
1002 if (data.ContainsKey("ownerid"))
1003 {
1004 ownerId = Convert.ToUInt64(data["ownerid"]);
1005 }
1006
1007 if (CheckPlaced(prefabname, pos, rot))
1008 continue;
1009
1010 if (prefabname.Contains("pillar"))
1011 continue;
1012
1013 // 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.
1014 if (prefabname.Contains("locks"))
1015 continue;
1016
1017 var entity = GameManager.server.CreateEntity(prefabname, pos, rot);
1018
1019 if (entity == null)
1020 continue;
1021
1022 entity.transform.position = pos;
1023 entity.transform.rotation = rot;
1024
1025 if (player != null)
1026 entity.SendMessage("SetDeployedBy", player, SendMessageOptions.DontRequireReceiver);
1027
1028 if (pasteData.Ownership)
1029 entity.OwnerID = ownerId;
1030
1031 var buildingBlock = entity as BuildingBlock;
1032
1033 if (buildingBlock != null)
1034 {
1035 buildingBlock.blockDefinition = PrefabAttribute.server.Find<Construction>(buildingBlock.prefabID);
1036 buildingBlock.SetGrade((BuildingGrade.Enum) data["grade"]);
1037 if (!pasteData.Stability)
1038 buildingBlock.grounded = true;
1039
1040 }
1041
1042 var decayEntity = entity as DecayEntity;
1043
1044 if (decayEntity != null)
1045 {
1046 if (pasteData.BuildingId == 0)
1047 pasteData.BuildingId = BuildingManager.server.NewBuildingID();
1048
1049 decayEntity.AttachToBuilding(pasteData.BuildingId);
1050 }
1051
1052 var stabilityEntity = entity as StabilityEntity;
1053
1054 if (stabilityEntity != null)
1055 {
1056 if (!stabilityEntity.grounded)
1057 {
1058 stabilityEntity.grounded = true;
1059 pasteData.StabilityEntities.Add(stabilityEntity);
1060 }
1061 }
1062
1063 entity.skinID = skinid;
1064 entity.Spawn();
1065
1066 var baseCombat = entity as BaseCombatEntity;
1067
1068 if (baseCombat != null)
1069 baseCombat.SetHealth(baseCombat.MaxHealth());
1070
1071 pasteData.PastedEntities.AddRange(TryPasteSlots(entity, data, pasteData));
1072
1073 var box = entity as StorageContainer;
1074 if (box != null)
1075 {
1076 box.inventory.Clear();
1077
1078 var items = new List<object>();
1079
1080 if (data.ContainsKey("items"))
1081 items = data["items"] as List<object>;
1082
1083 foreach (var itemDef in items)
1084 {
1085 var item = itemDef as Dictionary<string, object>;
1086 var itemid = Convert.ToInt32(item["id"]);
1087 var itemamount = Convert.ToInt32(item["amount"]);
1088 var itemskin = ulong.Parse(item["skinid"].ToString());
1089 var itemcondition = Convert.ToSingle(item["condition"]);
1090
1091 if (pasteData.IsItemReplace)
1092 itemid = GetItemId(itemid);
1093
1094 var i = ItemManager.CreateByItemID(itemid, itemamount, itemskin);
1095
1096 if (i != null)
1097 {
1098 i.condition = itemcondition;
1099
1100 if (item.ContainsKey("text"))
1101 i.text = item["text"].ToString();
1102
1103 if (item.ContainsKey("blueprintTarget"))
1104 {
1105 var blueprintTarget = Convert.ToInt32(item["blueprintTarget"]);
1106
1107 if (pasteData.IsItemReplace)
1108 blueprintTarget = GetItemId(blueprintTarget);
1109
1110 i.blueprintTarget = blueprintTarget;
1111 }
1112
1113 if (item.ContainsKey("magazine"))
1114 {
1115 var heldent = i.GetHeldEntity();
1116
1117 if (heldent != null)
1118 {
1119 var projectiles = heldent.GetComponent<BaseProjectile>();
1120
1121 if (projectiles != null)
1122 {
1123 var magazine = item["magazine"] as Dictionary<string, object>;
1124 var ammotype = int.Parse(magazine.Keys.ToArray()[0]);
1125 var ammoamount = int.Parse(magazine[ammotype.ToString()].ToString());
1126
1127 if (pasteData.IsItemReplace)
1128 ammotype = GetItemId(ammotype);
1129
1130 projectiles.primaryMagazine.ammoType = ItemManager.FindItemDefinition(ammotype);
1131 projectiles.primaryMagazine.contents = ammoamount;
1132 }
1133
1134 //TODO Doesn't add water to some containers
1135
1136 if (item.ContainsKey("items"))
1137 {
1138 var itemContainsList = item["items"] as List<object>;
1139
1140 foreach (var itemContains in itemContainsList)
1141 {
1142 var contents = itemContains as Dictionary<string, object>;
1143
1144 var contentsItemId = Convert.ToInt32(contents["id"]);
1145
1146 if (pasteData.IsItemReplace)
1147 contentsItemId = GetItemId(contentsItemId);
1148
1149 i.contents.AddItem(ItemManager.FindItemDefinition(contentsItemId),
1150 Convert.ToInt32(contents["amount"]));
1151 }
1152 }
1153 }
1154 }
1155
1156 var targetPos = -1;
1157
1158 if (item.ContainsKey("position"))
1159 targetPos = Convert.ToInt32(item["position"]);
1160
1161 i.position = targetPos;
1162 box.inventory.Insert(i);
1163 }
1164 }
1165 }
1166
1167 var autoTurret = entity as AutoTurret;
1168 if (autoTurret != null)
1169 {
1170 var authorizedPlayers = new List<ulong>();
1171
1172 if (data.ContainsKey("autoturret"))
1173 {
1174 var autoTurretData = data["autoturret"] as Dictionary<string, object>;
1175 authorizedPlayers = (autoTurretData["authorizedPlayers"] as List<object>)
1176 .Select(Convert.ToUInt64).ToList();
1177 }
1178
1179 if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth)
1180 authorizedPlayers.Add(player.userID);
1181
1182 foreach (var userId in authorizedPlayers)
1183 {
1184 autoTurret.authorizedPlayers.Add(new PlayerNameID
1185 {
1186 userid = Convert.ToUInt64(userId),
1187 username = "Player"
1188 });
1189 }
1190
1191 autoTurret.SendNetworkUpdate();
1192 }
1193
1194 var containerIo = entity as ContainerIOEntity;
1195 if (containerIo != null)
1196 {
1197 containerIo.inventory.Clear();
1198
1199 var items = new List<object>();
1200
1201 if (data.ContainsKey("items"))
1202 items = data["items"] as List<object>;
1203
1204 foreach (var itemDef in items)
1205 {
1206 var itemJson = itemDef as Dictionary<string, object>;
1207 var itemid = Convert.ToInt32(itemJson["id"]);
1208 var itemamount = Convert.ToInt32(itemJson["amount"]);
1209 var itemskin = ulong.Parse(itemJson["skinid"].ToString());
1210 var itemcondition = Convert.ToSingle(itemJson["condition"]);
1211
1212 if (pasteData.IsItemReplace)
1213 itemid = GetItemId(itemid);
1214
1215 var item = ItemManager.CreateByItemID(itemid, itemamount, itemskin);
1216
1217 if (item != null)
1218 {
1219 item.condition = itemcondition;
1220
1221 if (itemJson.ContainsKey("text"))
1222 item.text = itemJson["text"].ToString();
1223
1224 if (itemJson.ContainsKey("blueprintTarget"))
1225 {
1226 var blueprintTarget = Convert.ToInt32(itemJson["blueprintTarget"]);
1227
1228 if (pasteData.IsItemReplace)
1229 blueprintTarget = GetItemId(blueprintTarget);
1230
1231 item.blueprintTarget = blueprintTarget;
1232 }
1233
1234 if (itemJson.ContainsKey("magazine"))
1235 {
1236 var heldent = item.GetHeldEntity();
1237
1238 if (heldent != null)
1239 {
1240 var projectiles = heldent.GetComponent<BaseProjectile>();
1241
1242 if (projectiles != null)
1243 {
1244 var magazine = itemJson["magazine"] as Dictionary<string, object>;
1245 var ammotype = int.Parse(magazine.Keys.ToArray()[0]);
1246 var ammoamount = int.Parse(magazine[ammotype.ToString()].ToString());
1247
1248 if (pasteData.IsItemReplace)
1249 ammotype = GetItemId(ammotype);
1250
1251 projectiles.primaryMagazine.ammoType = ItemManager.FindItemDefinition(ammotype);
1252 projectiles.primaryMagazine.contents = ammoamount;
1253 }
1254
1255 //TODO Doesn't add water to some containers
1256
1257 if (itemJson.ContainsKey("items"))
1258 {
1259 var itemContainsList = itemJson["items"] as List<object>;
1260
1261 foreach (var itemContains in itemContainsList)
1262 {
1263 var contents = itemContains as Dictionary<string, object>;
1264
1265 var contentsItemId = Convert.ToInt32(contents["id"]);
1266
1267 if (pasteData.IsItemReplace)
1268 contentsItemId = GetItemId(contentsItemId);
1269
1270 item.contents.AddItem(ItemManager.FindItemDefinition(contentsItemId),
1271 Convert.ToInt32(contents["amount"]));
1272 }
1273 }
1274 }
1275 }
1276
1277 var targetPos = -1;
1278 if (itemJson.ContainsKey("position"))
1279 targetPos = Convert.ToInt32(itemJson["position"]);
1280
1281 item.position = targetPos;
1282 containerIo.inventory.Insert(item);
1283 }
1284 }
1285
1286 if (autoTurret != null)
1287 {
1288 autoTurret.Invoke(autoTurret.UpdateAttachedWeapon, 0.5f);
1289 }
1290
1291 containerIo.SendNetworkUpdate();
1292 }
1293
1294 var sign = entity as Signage;
1295 if (sign != null && data.ContainsKey("sign"))
1296 {
1297 var signData = data["sign"] as Dictionary<string, object>;
1298
1299 if (signData.ContainsKey("texture"))
1300 {
1301 var imageBytes = Convert.FromBase64String(signData["texture"].ToString());
1302
1303 FixSignage(sign, imageBytes);
1304 }
1305
1306 if (Convert.ToBoolean(signData["locked"]))
1307 sign.SetFlag(BaseEntity.Flags.Locked, true);
1308
1309 sign.SendNetworkUpdate();
1310 }
1311
1312 var sleepingBag = entity as SleepingBag;
1313 if (sleepingBag != null && data.ContainsKey("sleepingbag"))
1314 {
1315 var bagData = data["sleepingbag"] as Dictionary<string, object>;
1316
1317 sleepingBag.niceName = bagData["niceName"].ToString();
1318 sleepingBag.deployerUserID = ulong.Parse(bagData["deployerUserID"].ToString());
1319 sleepingBag.SetPublic(Convert.ToBoolean(bagData["isPublic"]));
1320 }
1321
1322 var cupboard = entity as BuildingPrivlidge;
1323 if (cupboard != null)
1324 {
1325 var authorizedPlayers = new List<ulong>();
1326
1327 if (data.ContainsKey("cupboard"))
1328 {
1329 var cupboardData = data["cupboard"] as Dictionary<string, object>;
1330 authorizedPlayers = (cupboardData["authorizedPlayers"] as List<object>).Select(Convert.ToUInt64)
1331 .ToList();
1332 }
1333
1334 if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth)
1335 authorizedPlayers.Add(player.userID);
1336
1337 foreach (var userId in authorizedPlayers)
1338 {
1339 cupboard.authorizedPlayers.Add(new PlayerNameID
1340 {
1341 userid = Convert.ToUInt64(userId),
1342 username = "Player"
1343 });
1344 }
1345
1346 cupboard.SendNetworkUpdate();
1347 }
1348
1349 var cctv_RC = entity as CCTV_RC;
1350
1351 if (cctv_RC != null && data.ContainsKey("cctv"))
1352 {
1353 var cctv = (Dictionary<string, object>)data["cctv"];
1354 cctv_RC.yawAmount = Convert.ToSingle(cctv["yaw"]);
1355 cctv_RC.pitchAmount = Convert.ToSingle(cctv["pitch"]);
1356 cctv_RC.rcIdentifier = cctv["rcIdentifier"].ToString();
1357 cctv_RC.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
1358 }
1359
1360 var vendingMachine = entity as VendingMachine;
1361 if (vendingMachine != null && data.ContainsKey("vendingmachine"))
1362 {
1363 var vendingData = data["vendingmachine"] as Dictionary<string, object>;
1364
1365 vendingMachine.shopName = vendingData["shopName"].ToString();
1366 vendingMachine.SetFlag(BaseEntity.Flags.Reserved4,
1367 Convert.ToBoolean(vendingData["isBroadcasting"]));
1368
1369 var sellOrders = vendingData["sellOrders"] as List<object>;
1370
1371 foreach (var orderPreInfo in sellOrders)
1372 {
1373 var orderInfo = orderPreInfo as Dictionary<string, object>;
1374
1375 if (!orderInfo.ContainsKey("inStock"))
1376 {
1377 orderInfo["inStock"] = 0;
1378 orderInfo["currencyIsBP"] = false;
1379 orderInfo["itemToSellIsBP"] = false;
1380 }
1381
1382 int itemToSellId = Convert.ToInt32(orderInfo["itemToSellID"]),
1383 currencyId = Convert.ToInt32(orderInfo["currencyID"]);
1384
1385 if (pasteData.IsItemReplace)
1386 {
1387 itemToSellId = GetItemId(itemToSellId);
1388 currencyId = GetItemId(currencyId);
1389 }
1390
1391 vendingMachine.sellOrders.sellOrders.Add(new ProtoBuf.VendingMachine.SellOrder
1392 {
1393 ShouldPool = false,
1394 itemToSellID = itemToSellId,
1395 itemToSellAmount = Convert.ToInt32(orderInfo["itemToSellAmount"]),
1396 currencyID = currencyId,
1397 currencyAmountPerItem = Convert.ToInt32(orderInfo["currencyAmountPerItem"]),
1398 inStock = Convert.ToInt32(orderInfo["inStock"]),
1399 currencyIsBP = Convert.ToBoolean(orderInfo["currencyIsBP"]),
1400 itemToSellIsBP = Convert.ToBoolean(orderInfo["itemToSellIsBP"])
1401 });
1402 }
1403
1404 vendingMachine.FullUpdate();
1405 }
1406
1407 var ioEntity = entity as IOEntity;
1408
1409 if (ioEntity != null)
1410 {
1411 var ioData = new Dictionary<string, object>();
1412
1413 if (data.ContainsKey("IOEntity"))
1414 {
1415 ioData = data["IOEntity"] as Dictionary<string, object> ?? new Dictionary<string, object>();
1416 }
1417
1418 ioData.Add("entity", ioEntity);
1419 ioData.Add("newId", ioEntity.net.ID);
1420
1421 object oldIdObject = 0;
1422 if (ioData.TryGetValue("oldID", out oldIdObject))
1423 {
1424 var oldId = Convert.ToUInt32(oldIdObject);
1425 pasteData.IoEntities.Add(oldId, ioData);
1426 }
1427 }
1428
1429 var flagsData = new Dictionary<string, object>();
1430
1431 if (data.ContainsKey("flags"))
1432 flagsData = data["flags"] as Dictionary<string, object>;
1433
1434 var flags = new Dictionary<BaseEntity.Flags, bool>();
1435
1436 foreach (var flagData in flagsData)
1437 {
1438 BaseEntity.Flags baseFlag;
1439 if (Enum.TryParse(flagData.Key, out baseFlag))
1440 flags.Add(baseFlag, Convert.ToBoolean(flagData.Value));
1441 }
1442
1443 foreach (var flag in flags)
1444 {
1445 entity.SetFlag(flag.Key, flag.Value);
1446 }
1447
1448 pasteData.PastedEntities.Add(entity);
1449 }
1450
1451 if (entities.Count > 0)
1452 NextTick(() => PasteLoop(pasteData));
1453 else
1454 {
1455 foreach (var ioData in pasteData.IoEntities.Values.ToArray())
1456 {
1457 if (!ioData.ContainsKey("entity"))
1458 continue;
1459
1460
1461 var ioEntity = ioData["entity"] as IOEntity;
1462
1463 List<object> inputs = null;
1464 if (ioData.ContainsKey("inputs"))
1465 inputs = ioData["inputs"] as List<object>;
1466
1467 var electricalBranch = ioEntity as ElectricalBranch;
1468 if (electricalBranch != null && ioData.ContainsKey("branchAmount"))
1469 {
1470 electricalBranch.branchAmount = Convert.ToInt32(ioData["branchAmount"]);
1471 }
1472
1473 // Realized counter.targetCounterNumber is private, leaving it in in case signature changes.
1474 var counter = ioEntity.GetComponentInParent<PowerCounter>();
1475 if (counter != null && ioData.ContainsKey("targetNumber"))
1476 {
1477 counter.targetCounterNumber = Convert.ToInt32(ioData["targetNumber"]);
1478 }
1479
1480 var timer = ioEntity as TimerSwitch;
1481 if (timer != null && ioData.ContainsKey("timerLength"))
1482 {
1483 timer.timerLength = Convert.ToInt32(ioData["timerLength"]);
1484 }
1485
1486 var rfBroadcaster = ioEntity as RFBroadcaster;
1487 if (rfBroadcaster != null && ioData.ContainsKey("frequency"))
1488 {
1489 rfBroadcaster.frequency = Convert.ToInt32(ioData["frequency"]);
1490 }
1491
1492 var rfReceiver = ioEntity as RFReceiver;
1493 if (rfReceiver != null && ioData.ContainsKey("frequency"))
1494 {
1495 rfReceiver.frequency = Convert.ToInt32(ioData["frequency"]);
1496 }
1497
1498 var doorManipulator = ioEntity as CustomDoorManipulator;
1499 if (doorManipulator != null)
1500 {
1501 var door = doorManipulator.FindDoor();
1502 doorManipulator.SetTargetDoor(door);
1503 }
1504
1505 if (inputs != null && inputs.Count > 0)
1506 {
1507 for (var index = 0; index < inputs.Count; index++)
1508 {
1509 var input = inputs[index] as Dictionary<string, object>;
1510 object oldIdObject;
1511 if (!input.TryGetValue("connectedID", out oldIdObject))
1512 continue;
1513
1514 var oldId = Convert.ToUInt32(oldIdObject);
1515
1516 if (oldId != 0 && pasteData.IoEntities.ContainsKey(oldId))
1517 {
1518 if (ioEntity.inputs[index] == null)
1519 ioEntity.inputs[index] = new IOEntity.IOSlot();
1520
1521 var ioConnection = pasteData.IoEntities[oldId];
1522
1523 object temp;
1524
1525 if (ioConnection.ContainsKey("newId"))
1526 {
1527 ioEntity.inputs[index].connectedTo.entityRef.uid =
1528 Convert.ToUInt32(ioConnection["newId"]);
1529 }
1530 }
1531 }
1532 }
1533
1534 List<object> outputs = null;
1535 if (ioData.ContainsKey("outputs"))
1536 outputs = ioData["outputs"] as List<object>;
1537
1538 if (outputs != null && outputs.Count > 0)
1539 {
1540 for (var index = 0; index < outputs.Count; index++)
1541 {
1542 var output = outputs[index] as Dictionary<string, object>;
1543 var oldId = Convert.ToUInt32(output["connectedID"]);
1544
1545 if (oldId != 0 && pasteData.IoEntities.ContainsKey(oldId))
1546 {
1547 if (ioEntity.outputs[index] == null)
1548 ioEntity.outputs[index] = new IOEntity.IOSlot();
1549
1550 var ioConnection = pasteData.IoEntities[oldId];
1551
1552 if (ioConnection.ContainsKey("newId"))
1553 {
1554 var ioEntity2 = ioConnection["entity"] as IOEntity;
1555 var connectedToSlot = Convert.ToInt32(output["connectedToSlot"]);
1556 var ioOutput = ioEntity.outputs[index];
1557
1558 ioOutput.connectedTo = new IOEntity.IORef();
1559 ioOutput.connectedTo.Set(ioEntity2);
1560 ioOutput.connectedToSlot = connectedToSlot;
1561 ioOutput.connectedTo.Init();
1562
1563 ioEntity2.inputs[connectedToSlot].connectedTo = new IOEntity.IORef();
1564 ioEntity2.inputs[connectedToSlot].connectedTo.Set(ioEntity);
1565 ioEntity2.inputs[connectedToSlot].connectedToSlot = index;
1566 ioEntity2.inputs[connectedToSlot].connectedTo.Init();
1567
1568 ioOutput.niceName = output["niceName"] as string;
1569
1570 ioOutput.type = (IOEntity.IOType) Convert.ToInt32(output["type"]);
1571 }
1572
1573 if (output.ContainsKey("linePoints"))
1574 {
1575 var linePoints = output["linePoints"] as List<object>;
1576 if (linePoints != null)
1577 {
1578 var lineList = new List<Vector3>();
1579 foreach (var point in linePoints)
1580 {
1581 var linePoint = point as Dictionary<string, object>;
1582 lineList.Add(new Vector3(
1583 Convert.ToSingle(linePoint["x"]),
1584 Convert.ToSingle(linePoint["y"]),
1585 Convert.ToSingle(linePoint["z"])));
1586 }
1587
1588 ioEntity.outputs[index].linePoints = lineList.ToArray();
1589 }
1590 }
1591 }
1592 }
1593 }
1594
1595 ioEntity.MarkDirtyForceUpdateOutputs();
1596 ioEntity.SendNetworkUpdate();
1597 }
1598
1599 foreach (var entity in pasteData.StabilityEntities)
1600 {
1601 entity.grounded = false;
1602 entity.InitializeSupports();
1603 entity.UpdateStability();
1604 }
1605
1606 if (player != null)
1607 {
1608 SendReply(player, Lang("PASTE_SUCCESS", player.UserIDString));
1609#if DEBUG
1610 SendReply(player, $"Stopwatch took: {pasteData.Sw.Elapsed.TotalMilliseconds} ms");
1611#endif
1612 }
1613 else
1614 {
1615 Puts(Lang("PASTE_SUCCESS"));
1616 }
1617
1618 if (!_lastPastes.ContainsKey(player?.UserIDString ?? _serverId))
1619 _lastPastes[player?.UserIDString ?? _serverId] = new Stack<List<BaseEntity>>();
1620
1621 _lastPastes[player?.UserIDString ?? _serverId].Push(pasteData.PastedEntities);
1622
1623 pasteData.Callback?.Invoke();
1624
1625 Interface.CallHook("OnPasteFinished", pasteData.PastedEntities);
1626 }
1627 }
1628
1629 private HashSet<Dictionary<string, object>> PreLoadData(List<object> entities, Vector3 startPos,
1630 float rotationCorrection, bool deployables, bool inventories, bool auth, bool vending)
1631 {
1632 var eulerRotation = new Vector3(0f, rotationCorrection, 0f);
1633 var quaternionRotation = Quaternion.EulerRotation(eulerRotation);
1634 var preloaddata = new HashSet<Dictionary<string, object>>();
1635
1636 foreach (var entity in entities)
1637 {
1638 var data = entity as Dictionary<string, object>;
1639
1640 if (!deployables && !data.ContainsKey("grade"))
1641 continue;
1642
1643 var pos = (Dictionary<string, object>) data["pos"];
1644 var rot = (Dictionary<string, object>) data["rot"];
1645
1646 data.Add("position",
1647 quaternionRotation * (new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]),
1648 Convert.ToSingle(pos["z"]))) + startPos);
1649 data.Add("rotation",
1650 Quaternion.EulerRotation(eulerRotation + new Vector3(Convert.ToSingle(rot["x"]),
1651 Convert.ToSingle(rot["y"]), Convert.ToSingle(rot["z"]))));
1652
1653 if (!inventories && data.ContainsKey("items"))
1654 data["items"] = new List<object>();
1655
1656 if (!vending && data["prefabname"].ToString().Contains("vendingmachine"))
1657 data.Remove("vendingmachine");
1658
1659 preloaddata.Add(data);
1660 }
1661
1662 return preloaddata;
1663 }
1664
1665 private object TryCopy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection,
1666 string[] args, BasePlayer player, Action callback)
1667 {
1668 bool saveShare = _config.Copy.Share, saveTree = _config.Copy.Tree, eachToEach = _config.Copy.EachToEach;
1669 var copyMechanics = CopyMechanics.Proximity;
1670 var radius = _config.Copy.Radius;
1671
1672 for (var i = 0;; i = i + 2)
1673 {
1674 if (i >= args.Length)
1675 break;
1676
1677 var valueIndex = i + 1;
1678
1679 if (valueIndex >= args.Length)
1680 return Lang("SYNTAX_COPY");
1681
1682 var param = args[i].ToLower();
1683
1684 switch (param)
1685 {
1686 case "e":
1687 case "each":
1688 if (!bool.TryParse(args[valueIndex], out eachToEach))
1689 return Lang("SYNTAX_BOOL", null, param);
1690
1691 break;
1692
1693 case "m":
1694 case "method":
1695 switch (args[valueIndex].ToLower())
1696 {
1697 case "b":
1698 case "building":
1699 copyMechanics = CopyMechanics.Building;
1700 break;
1701
1702 case "p":
1703 case "proximity":
1704 copyMechanics = CopyMechanics.Proximity;
1705 break;
1706 }
1707
1708 break;
1709
1710 case "r":
1711 case "radius":
1712 if (!float.TryParse(args[valueIndex], out radius))
1713 return Lang("SYNTAX_RADIUS");
1714
1715 break;
1716
1717 case "s":
1718 case "share":
1719 if (!bool.TryParse(args[valueIndex], out saveShare))
1720 return Lang("SYNTAX_BOOL", null, param);
1721
1722 break;
1723
1724 case "t":
1725 case "tree":
1726 if (!bool.TryParse(args[valueIndex], out saveTree))
1727 return Lang("SYNTAX_BOOL", null, param);
1728
1729 break;
1730
1731 default:
1732 return Lang("SYNTAX_COPY");
1733 }
1734 }
1735
1736 Copy(sourcePos, sourceRot, filename, rotationCorrection, copyMechanics, radius, saveTree, saveShare,
1737 eachToEach, player, callback);
1738
1739 return true;
1740 }
1741
1742 private void TryCopySlots(BaseEntity ent, IDictionary<string, object> housedata, bool saveShare)
1743 {
1744 foreach (var slot in _checkSlots)
1745 {
1746 if (!ent.HasSlot(slot))
1747 continue;
1748
1749 var slotEntity = ent.GetSlot(slot);
1750
1751 if (slotEntity == null)
1752 continue;
1753
1754 var codedata = new Dictionary<string, object>
1755 {
1756 {"prefabname", slotEntity.PrefabName},
1757 {"flags", TryCopyFlags(ent)}
1758 };
1759
1760 if (slotEntity.GetComponent<CodeLock>())
1761 {
1762 var codeLock = slotEntity.GetComponent<CodeLock>();
1763
1764 codedata.Add("code", codeLock.code);
1765
1766 if (saveShare)
1767 codedata.Add("whitelistPlayers", codeLock.whitelistPlayers);
1768
1769 if (codeLock.guestCode != null && codeLock.guestCode.Length == 4)
1770 {
1771 codedata.Add("guestCode", codeLock.guestCode);
1772
1773 if (saveShare)
1774 codedata.Add("guestPlayers", codeLock.guestPlayers);
1775 }
1776 }
1777 else if (slotEntity.GetComponent<KeyLock>())
1778 {
1779 var keyLock = slotEntity.GetComponent<KeyLock>();
1780 var code = keyLock.keyCode;
1781
1782 if (keyLock.firstKeyCreated)
1783 code |= 0x80;
1784
1785 codedata.Add("ownerId", keyLock.OwnerID.ToString());
1786 codedata.Add("code", code.ToString());
1787 }
1788
1789 var slotName = slot.ToString().ToLower();
1790
1791 housedata.Add(slotName, codedata);
1792 }
1793 }
1794
1795 private Dictionary<string, object> TryCopyFlags(BaseEntity entity)
1796 {
1797 var flags = new Dictionary<string, object>();
1798
1799 foreach (BaseEntity.Flags flag in Enum.GetValues(typeof(BaseEntity.Flags)))
1800 {
1801 if (!_config.DataSaving || entity.HasFlag(flag))
1802 flags.Add(flag.ToString(), entity.HasFlag(flag));
1803 }
1804
1805 return flags;
1806 }
1807
1808 private object TryPaste(Vector3 startPos, string filename, BasePlayer player, float rotationCorrection,
1809 string[] args, bool autoHeight = true, Action callback = null)
1810 {
1811 var userId = player?.UserIDString;
1812
1813 var path = _subDirectory + filename;
1814
1815 if (!Interface.Oxide.DataFileSystem.ExistsDatafile(path))
1816 return Lang("FILE_NOT_EXISTS", userId);
1817
1818 var data = Interface.Oxide.DataFileSystem.GetDatafile(path);
1819
1820 if (data["default"] == null || data["entities"] == null)
1821 return Lang("FILE_BROKEN", userId);
1822
1823 float heightAdj = 0f, blockCollision = 0f;
1824 bool auth = _config.Paste.Auth,
1825 inventories = _config.Paste.Inventories,
1826 deployables = _config.Paste.Deployables,
1827 vending = _config.Paste.VendingMachines,
1828 stability = _config.Paste.Stability,
1829 ownership = _config.Paste.EntityOwner;
1830
1831 for (var i = 0;; i = i + 2)
1832 {
1833 if (i >= args.Length)
1834 break;
1835
1836 var valueIndex = i + 1;
1837
1838 if (valueIndex >= args.Length)
1839 return Lang("SYNTAX_PASTE_OR_PASTEBACK", userId);
1840
1841 var param = args[i].ToLower();
1842
1843 switch (param)
1844 {
1845 case "a":
1846 case "auth":
1847 if (!bool.TryParse(args[valueIndex], out auth))
1848 return Lang("SYNTAX_BOOL", userId, param);
1849
1850 break;
1851
1852 case "b":
1853 case "blockcollision":
1854 if (!float.TryParse(args[valueIndex], out blockCollision))
1855 return Lang("SYNTAX_BLOCKCOLLISION", userId);
1856
1857 break;
1858
1859 case "d":
1860 case "deployables":
1861 if (!bool.TryParse(args[valueIndex], out deployables))
1862 return Lang("SYNTAX_BOOL", userId, param);
1863
1864 break;
1865
1866 case "h":
1867 case "height":
1868 if (!float.TryParse(args[valueIndex], out heightAdj))
1869 return Lang("SYNTAX_HEIGHT", userId);
1870
1871 break;
1872
1873 case "i":
1874 case "inventories":
1875 if (!bool.TryParse(args[valueIndex], out inventories))
1876 return Lang("SYNTAX_BOOL", userId, param);
1877
1878 break;
1879
1880 case "s":
1881 case "stability":
1882 if (!bool.TryParse(args[valueIndex], out stability))
1883 return Lang("SYNTAX_BOOL", userId, param);
1884
1885 break;
1886
1887 case "v":
1888 case "vending":
1889 if (!bool.TryParse(args[valueIndex], out vending))
1890 return Lang("SYNTAX_BOOL", userId, param);
1891
1892 break;
1893
1894 case "o":
1895 case "entityowner":
1896 if (!bool.TryParse(args[valueIndex], out ownership))
1897 return Lang("SYNTAX_BOOL", userId, param);
1898
1899 break;
1900
1901 case "autoheight":
1902 if (!bool.TryParse(args[valueIndex], out autoHeight))
1903 return Lang("SYNTAX_BOOL", userId, param);
1904
1905 break;
1906
1907 default:
1908 return Lang("SYNTAX_PASTE_OR_PASTEBACK", userId);
1909 }
1910 }
1911
1912 startPos.y += heightAdj;
1913
1914 var preloadData = PreLoadData(data["entities"] as List<object>, startPos, rotationCorrection, deployables,
1915 inventories, auth, vending);
1916
1917 if (autoHeight)
1918 {
1919 var bestHeight = FindBestHeight(preloadData, startPos);
1920
1921 if (bestHeight is string)
1922 return bestHeight;
1923
1924 heightAdj += ((float) bestHeight - startPos.y);
1925
1926 foreach (var entity in preloadData)
1927 {
1928 var pos = ((Vector3) entity["position"]);
1929 pos.y += heightAdj;
1930
1931 entity["position"] = pos;
1932 }
1933 }
1934
1935 if (blockCollision > 0f)
1936 {
1937 var collision = CheckCollision(preloadData, startPos, blockCollision);
1938
1939 if (collision is string)
1940 return collision;
1941 }
1942
1943 var protocol = new Dictionary<string, object>();
1944
1945 if (data["protocol"] != null)
1946 protocol = data["protocol"] as Dictionary<string, object>;
1947
1948 Paste(preloadData, protocol, ownership, startPos, player, stability, rotationCorrection,
1949 autoHeight ? heightAdj : 0, auth, callback);
1950 return true;
1951 }
1952
1953 private List<BaseEntity> TryPasteSlots(BaseEntity ent, Dictionary<string, object> structure,
1954 PasteData pasteData)
1955 {
1956 var entitySlots = new List<BaseEntity>();
1957
1958 foreach (var slot in _checkSlots)
1959 {
1960 var slotName = slot.ToString().ToLower();
1961
1962 if (!ent.HasSlot(slot) || !structure.ContainsKey(slotName))
1963 continue;
1964
1965 var slotData = structure[slotName] as Dictionary<string, object>;
1966 var slotEntity = GameManager.server.CreateEntity((string) slotData["prefabname"], Vector3.zero);
1967
1968 if (slotEntity == null)
1969 continue;
1970
1971 slotEntity.gameObject.Identity();
1972 slotEntity.SetParent(ent, slotName);
1973 slotEntity.OnDeployed(ent, null);
1974 slotEntity.Spawn();
1975
1976 ent.SetSlot(slot, slotEntity);
1977
1978 entitySlots.Add(slotEntity);
1979
1980 if (slotName != "lock" || !slotData.ContainsKey("code"))
1981 continue;
1982
1983 if (slotEntity.GetComponent<CodeLock>())
1984 {
1985 var code = (string) slotData["code"];
1986
1987 if (!string.IsNullOrEmpty(code))
1988 {
1989 var codeLock = slotEntity.GetComponent<CodeLock>();
1990 codeLock.code = code;
1991 codeLock.hasCode = true;
1992
1993 if (pasteData.Auth && pasteData.Player != null)
1994 codeLock.whitelistPlayers.Add(pasteData.Player.userID);
1995
1996 if (slotData.ContainsKey("whitelistPlayers"))
1997 {
1998 foreach (var userId in slotData["whitelistPlayers"] as List<object>)
1999 {
2000 codeLock.whitelistPlayers.Add(Convert.ToUInt64(userId));
2001 }
2002 }
2003
2004 if (slotData.ContainsKey("guestCode"))
2005 {
2006 var guestCode = (string) slotData["guestCode"];
2007
2008 codeLock.guestCode = guestCode;
2009 codeLock.hasGuestCode = true;
2010
2011 if (slotData.ContainsKey("guestPlayers"))
2012 {
2013 foreach (var userId in slotData["guestPlayers"] as List<object>)
2014 {
2015 codeLock.guestPlayers.Add(Convert.ToUInt64(userId));
2016 }
2017 }
2018 }
2019
2020 codeLock.SetFlag(BaseEntity.Flags.Locked, true);
2021 }
2022 }
2023 else if (slotEntity.GetComponent<KeyLock>())
2024 {
2025 var code = Convert.ToInt32(slotData["code"]);
2026 var keyLock = slotEntity.GetComponent<KeyLock>();
2027
2028 if ((code & 0x80) != 0)
2029 {
2030 keyLock.keyCode = (code & 0x7F);
2031 keyLock.firstKeyCreated = true;
2032 keyLock.SetFlag(BaseEntity.Flags.Locked, true);
2033 }
2034
2035 if (pasteData.Ownership && slotData.ContainsKey("ownerId"))
2036 {
2037 keyLock.OwnerID = Convert.ToUInt64(slotData["ownerId"]);
2038 }
2039 }
2040 }
2041
2042 return entitySlots;
2043 }
2044
2045 private object TryPasteBack(string filename, BasePlayer player, string[] args)
2046 {
2047 var path = _subDirectory + filename;
2048
2049 if (!Interface.Oxide.DataFileSystem.ExistsDatafile(path))
2050 return Lang("FILE_NOT_EXISTS", player?.UserIDString);
2051
2052 var data = Interface.Oxide.DataFileSystem.GetDatafile(path);
2053
2054 if (data["default"] == null || data["entities"] == null)
2055 return Lang("FILE_BROKEN", player?.UserIDString);
2056
2057 var defaultdata = data["default"] as Dictionary<string, object>;
2058 var pos = defaultdata["position"] as Dictionary<string, object>;
2059 var rotationCorrection = Convert.ToSingle(defaultdata["rotationdiff"]);
2060 var startPos = new Vector3(Convert.ToSingle(pos["x"]), Convert.ToSingle(pos["y"]),
2061 Convert.ToSingle(pos["z"]));
2062
2063 return TryPaste(startPos, filename, player, rotationCorrection, args, autoHeight: false);
2064 }
2065
2066 //Сhat commands
2067
2068 [ChatCommand("copy")]
2069 private void CmdChatCopy(BasePlayer player, string command, string[] args)
2070 {
2071 if (!HasAccess(player, _copyPermission))
2072 {
2073 SendReply(player, Lang("NO_ACCESS", player.UserIDString));
2074 return;
2075 }
2076
2077 if (args.Length < 1)
2078 {
2079 SendReply(player, Lang("SYNTAX_COPY", player.UserIDString));
2080 return;
2081 }
2082
2083 var savename = args[0];
2084 var success = TryCopyFromSteamId(player.userID, savename, args.Skip(1).ToArray());
2085
2086 if (success is string)
2087 {
2088 SendReply(player, (string) success);
2089 }
2090 }
2091
2092 [ChatCommand("paste")]
2093 private void CmdChatPaste(BasePlayer player, string command, string[] args)
2094 {
2095 if (!HasAccess(player, _pastePermission))
2096 {
2097 SendReply(player, Lang("NO_ACCESS", player.UserIDString));
2098 return;
2099 }
2100
2101 if (args.Length < 1)
2102 {
2103 SendReply(player, Lang("SYNTAX_PASTE_OR_PASTEBACK", player.UserIDString));
2104 return;
2105 }
2106
2107 var success = TryPasteFromSteamId(player.userID, args[0], args.Skip(1).ToArray());
2108
2109 if (success is string)
2110 {
2111 SendReply(player, (string) success);
2112 }
2113 }
2114
2115 [ChatCommand("copylist")]
2116 private void CmdChatList(BasePlayer player, string command, string[] args)
2117 {
2118 if (!HasAccess(player, _listPermission))
2119 {
2120 SendReply(player, Lang("NO_ACCESS", player.UserIDString));
2121 return;
2122 }
2123
2124 var files = Interface.Oxide.DataFileSystem.GetFiles(_subDirectory);
2125
2126 var fileList = new List<string>();
2127
2128 foreach (var file in files)
2129 {
2130 var strFileParts = file.Split('/');
2131 var justfile = strFileParts[strFileParts.Length - 1].Replace(".json", "");
2132 fileList.Add(justfile);
2133 }
2134
2135 SendReply(player, Lang("AVAILABLE_STRUCTURES", player.UserIDString));
2136 SendReply(player, string.Join(", ", fileList.ToArray()));
2137 }
2138
2139 [ChatCommand("pasteback")]
2140 private void CmdChatPasteBack(BasePlayer player, string command, string[] args)
2141 {
2142 if (!HasAccess(player, _pastebackPermission))
2143 {
2144 SendReply(player, Lang("NO_ACCESS", player.UserIDString));
2145 return;
2146 }
2147
2148 var result = CmdPasteBack(player, args);
2149
2150 if (result is string)
2151 SendReply(player, (string) result);
2152 }
2153
2154 [ChatCommand("undo")]
2155 private void CmdChatUndo(BasePlayer player, string command, string[] args)
2156 {
2157 if (!HasAccess(player, _undoPermission))
2158 {
2159 SendReply(player, Lang("NO_ACCESS", player.UserIDString));
2160 return;
2161 }
2162
2163 CmdUndo(player.UserIDString, args);
2164 }
2165
2166 //Console commands [From Server]
2167
2168 [ConsoleCommand("pasteback")]
2169 private void CmdConsolePasteBack(ConsoleSystem.Arg arg)
2170 {
2171 if (!arg.IsAdmin)
2172 return;
2173
2174 var result = CmdPasteBack(arg.Player(), arg.Args);
2175
2176 if (result is string)
2177 SendReply(arg, (string) result);
2178 }
2179
2180 [ConsoleCommand("undo")]
2181 private void CmdConsoleUndo(ConsoleSystem.Arg arg)
2182 {
2183 if (!arg.IsAdmin)
2184 return;
2185
2186 var player = arg.Player();
2187
2188 CmdUndo(player == null ? _serverId : player.UserIDString, arg.Args);
2189 }
2190
2191 //Replace between old ItemID to new ItemID
2192
2193 private static readonly Dictionary<int, int> ReplaceItemId = new Dictionary<int, int>
2194 {
2195 {-1461508848, 1545779598},
2196 {2115555558, 588596902},
2197 {-533875561, 785728077},
2198 {1621541165, 51984655},
2199 {-422893115, -1691396643},
2200 {815896488, -1211166256},
2201 {805088543, -1321651331},
2202 {449771810, 605467368},
2203 {1152393492, 1712070256},
2204 {1578894260, -742865266},
2205 {1436532208, 1638322904},
2206 {542276424, -1841918730},
2207 {1594947829, -17123659},
2208 {-1035059994, -1685290200},
2209 {1818890814, -1036635990},
2210 {1819281075, -727717969},
2211 {1685058759, -1432674913},
2212 {93029210, 1548091822},
2213 {-1565095136, 352130972},
2214 {-1775362679, 215754713},
2215 {-1775249157, 14241751},
2216 {-1280058093, -1023065463},
2217 {-420273765, -1234735557},
2218 {563023711, -2139580305},
2219 {790921853, -262590403},
2220 {-337261910, -2072273936},
2221 {498312426, -1950721390},
2222 {504904386, 1655650836},
2223 {-1221200300, -559599960},
2224 {510887968, 15388698},
2225 {-814689390, 866889860},
2226 {1024486167, 1382263453},
2227 {2021568998, 609049394},
2228 {97329, 1099314009},
2229 {1046072789, -582782051},
2230 {97409, -1273339005},
2231 {-1480119738, -1262185308},
2232 {1611480185, 1931713481},
2233 {-1386464949, 1553078977},
2234 {93832698, 1776460938},
2235 {-1063412582, -586342290},
2236 {-1887162396, -996920608},
2237 {-55660037, 1588298435},
2238 {919780768, 1711033574},
2239 {-365801095, 1719978075},
2240 {68998734, 613961768},
2241 {-853695669, 1443579727},
2242 {271534758, 833533164},
2243 {-770311783, -180129657},
2244 {-1192532973, 1424075905},
2245 {-307490664, 1525520776},
2246 {707427396, 602741290},
2247 {707432758, -761829530},
2248 {-2079677721, 1783512007},
2249 {-1342405573, -1316706473},
2250 {-139769801, 1946219319},
2251 {-1043746011, -700591459},
2252 {2080339268, 1655979682},
2253 {-171664558, -1941646328},
2254 {1050986417, -1557377697},
2255 {-1693683664, 1789825282},
2256 {523409530, 1121925526},
2257 {1300054961, 634478325},
2258 {-2095387015, 1142993169},
2259 {1428021640, 1104520648},
2260 {94623429, 1534542921},
2261 {1436001773, -1938052175},
2262 {1711323399, 1973684065},
2263 {1734319168, -1848736516},
2264 {-1658459025, -1440987069},
2265 {-726947205, -751151717},
2266 {-341443994, 363467698},
2267 {1540879296, 2009734114},
2268 {94756378, -858312878},
2269 {3059095, 204391461},
2270 {3059624, 1367190888},
2271 {2045107609, -778875547},
2272 {583366917, 998894949},
2273 {2123300234, 1965232394},
2274 {1983936587, -321733511},
2275 {1257201758, -97956382},
2276 {-1144743963, 296519935},
2277 {-1144542967, -113413047},
2278 {-1144334585, -2022172587},
2279 {1066729526, -1101924344},
2280 {-1598790097, 1390353317},
2281 {-933236257, 1221063409},
2282 {-1575287163, -1336109173},
2283 {-2104481870, -2067472972},
2284 {-1571725662, 1353298668},
2285 {1456441506, 1729120840},
2286 {1200628767, -1112793865},
2287 {-778796102, 1409529282},
2288 {1526866730, 674734128},
2289 {1925723260, -1519126340},
2290 {1891056868, 1401987718},
2291 {1295154089, -1878475007},
2292 {498591726, 1248356124},
2293 {1755466030, -592016202},
2294 {726730162, 798638114},
2295 {-1034048911, -1018587433},
2296 {252529905, 274502203},
2297 {471582113, -1065444793},
2298 {-1138648591, 16333305},
2299 {305916740, 649305914},
2300 {305916742, 649305916},
2301 {305916744, 649305918},
2302 {1908328648, -1535621066},
2303 {-2078972355, 1668129151},
2304 {-533484654, 989925924},
2305 {1571660245, 1569882109},
2306 {1045869440, -1215753368},
2307 {1985408483, 528668503},
2308 {97513422, 304481038},
2309 {1496470781, -196667575},
2310 {1229879204, 952603248},
2311 {-1722829188, 936496778},
2312 {1849912854, 1948067030},
2313 {-1266285051, 1413014235},
2314 {-1749787215, -1000573653},
2315 {28178745, -946369541},
2316 {-505639592, -1999722522},
2317 {1598149413, -1992717673},
2318 {-1779401418, -691113464},
2319 {-57285700, -335089230},
2320 {98228420, 479143914},
2321 {1422845239, 999690781},
2322 {277631078, -1819763926},
2323 {115739308, 1366282552},
2324 {-522149009, -690276911},
2325 {3175989, -1899491405},
2326 {718197703, -746030907},
2327 {384204160, 1840822026},
2328 {-1308622549, 143803535},
2329 {-217113639, -2124352573},
2330 {-1580059655, -265876753},
2331 {-1832205789, 1070894649},
2332 {305916741, 649305917},
2333 {936777834, 3222790},
2334 {-1224598842, 200773292},
2335 {-1976561211, -1506397857},
2336 {-1406876421, 1675639563},
2337 {-1397343301, -23994173},
2338 {1260209393, 850280505},
2339 {-1035315940, 1877339384},
2340 {-1381682752, 1714496074},
2341 {696727039, -1022661119},
2342 {-2128719593, -803263829},
2343 {-1178289187, -1903165497},
2344 {1351172108, 1181207482},
2345 {-450738836, -1539025626},
2346 {-966287254, -324675402},
2347 {340009023, 671063303},
2348 {124310981, -1478212975},
2349 {1501403549, -2094954543},
2350 {698310895, -1252059217},
2351 {523855532, 1266491000},
2352 {2045246801, -886280491},
2353 {583506109, -237809779},
2354 {-148163128, 794356786},
2355 {-132588262, -1773144852},
2356 {-1666761111, 196700171},
2357 {-465236267, 442289265},
2358 {-1211618504, 1751045826},
2359 {2133577942, -1982036270},
2360 {-1014825244, -682687162},
2361 {-991829475, 1536610005},
2362 {-642008142, -1709878924},
2363 {661790782, 1272768630},
2364 {-1440143841, -1780802565},
2365 {569119686, 1746956556},
2366 {1404466285, -1102429027},
2367 {-1616887133, -48090175},
2368 {-1167640370, -1163532624},
2369 {-1284735799, 1242482355},
2370 {-1278649848, -1824943010},
2371 {776005741, 1814288539},
2372 {108061910, -316250604},
2373 {255101535, -1663759755},
2374 {-51678842, 1658229558},
2375 {-789202811, 254522515},
2376 {516382256, -132516482},
2377 {50834473, 1381010055},
2378 {-975723312, 1159991980},
2379 {1908195100, -850982208},
2380 {-1097452776, -110921842},
2381 {146685185, -1469578201},
2382 {-1716193401, -1812555177},
2383 {193190034, -2069578888},
2384 {371156815, -852563019},
2385 {3343606, -1966748496},
2386 {825308669, -1137865085},
2387 {830965940, -586784898},
2388 {1662628660, -163828118},
2389 {1662628661, -163828117},
2390 {1662628662, -163828112},
2391 {-1832205788, 1070894648},
2392 {-1832205786, 1070894646},
2393 {1625090418, 181590376},
2394 {-1269800768, -874975042},
2395 {429648208, -1190096326},
2396 {-1832205787, 1070894647},
2397 {-1832205785, 1070894645},
2398 {107868, 696029452},
2399 {997973965, -2012470695},
2400 {-46188931, -702051347},
2401 {-46848560, -194953424},
2402 {-2066726403, -989755543},
2403 {-2043730634, 1873897110},
2404 {1325935999, -1520560807},
2405 {-225234813, -78533081},
2406 {-202239044, -1509851560},
2407 {-322501005, 1422530437},
2408 {-1851058636, 1917703890},
2409 {-1828062867, -1162759543},
2410 {-1966381470, -1130350864},
2411 {968732481, 1391703481},
2412 {991728250, -242084766},
2413 {-253819519, 621915341},
2414 {-1714986849, 1827479659},
2415 {-1691991080, 813023040},
2416 {179448791, -395377963},
2417 {431617507, -1167031859},
2418 {688032252, 69511070},
2419 {-1059362949, -4031221},
2420 {1265861812, 1110385766},
2421 {374890416, 317398316},
2422 {1567404401, 1882709339},
2423 {-1057402571, 95950017},
2424 {-758925787, -1130709577},
2425 {-1411620422, 1052926200},
2426 {88869913, -542577259},
2427 {-2094080303, 1318558775},
2428 {843418712, -1962971928},
2429 {-1569356508, -1405508498},
2430 {-1569280852, 1478091698},
2431 {449769971, 1953903201},
2432 {590532217, -2097376851},
2433 {3387378, 1414245162},
2434 {1767561705, 1992974553},
2435 {106433500, 237239288},
2436 {-1334615971, -1778159885},
2437 {-135651869, 1722154847},
2438 {-1595790889, 1850456855},
2439 {-459156023, -1695367501},
2440 {106434956, -1779183908},
2441 {-578028723, -1302129395},
2442 {-586116979, 286193827},
2443 {-1379225193, -75944661},
2444 {-930579334, 649912614},
2445 {548699316, 818877484},
2446 {142147109, 1581210395},
2447 {148953073, 1903654061},
2448 {102672084, 980333378},
2449 {640562379, -1651220691},
2450 {-1732316031, -1622660759},
2451 {-2130280721, 756517185},
2452 {-1725510067, -722241321},
2453 {1974032895, -1673693549},
2454 {-225085592, -567909622},
2455 {509654999, 1898094925},
2456 {466113771, -1511285251},
2457 {2033918259, 1373971859},
2458 {2069925558, -1736356576},
2459 {-1026117678, 803222026},
2460 {1987447227, -1861522751},
2461 {540154065, -544317637},
2462 {1939428458, 176787552},
2463 {-288010497, -2002277461},
2464 {-847065290, 1199391518},
2465 {3506021, 963906841},
2466 {649603450, 442886268},
2467 {3506418, 1414245522},
2468 {569935070, -1104881824},
2469 {113284, -1985799200},
2470 {1916127949, -277057363},
2471 {-1775234707, -1978999529},
2472 {-388967316, 1326180354},
2473 {2007564590, -575483084},
2474 {-1705696613, 177226991},
2475 {670655301, -253079493},
2476 {1148128486, -1958316066},
2477 {-141135377, 567235583},
2478 {109266897, -932201673},
2479 {-527558546, 2087678962},
2480 {-1745053053, -904863145},
2481 {1223860752, 573926264},
2482 {-419069863, 1234880403},
2483 {-1617374968, -1994909036},
2484 {2057749608, 1950721418},
2485 {24576628, -2025184684},
2486 {-1659202509, 1608640313},
2487 {2107229499, -1549739227},
2488 {191795897, -765183617},
2489 {-1009492144, 795371088},
2490 {2077983581, -1367281941},
2491 {378365037, 352499047},
2492 {-529054135, -1199897169},
2493 {-529054134, -1199897172},
2494 {486166145, -1023374709},
2495 {1628490888, 23352662},
2496 {1498516223, 1205607945},
2497 {-632459882, -1647846966},
2498 {-626812403, -845557339},
2499 {385802761, -1370759135},
2500 {2117976603, 121049755},
2501 {1338515426, -996185386},
2502 {-1455694274, 98508942},
2503 {1579245182, 2070189026},
2504 {-587434450, 1521286012},
2505 {-163742043, 1542290441},
2506 {-1224714193, -1832422579},
2507 {644359987, 826309791},
2508 {-1962514734, -143132326},
2509 {-705305612, 1153652756},
2510 {-357728804, -1819233322},
2511 {-698499648, -1138208076},
2512 {1213686767, -1850571427},
2513 {386382445, -855748505},
2514 {1859976884, 553887414},
2515 {960793436, 996293980},
2516 {1001265731, 2048317869},
2517 {1253290621, -1754948969},
2518 {470729623, -1293296287},
2519 {1051155022, -369760990},
2520 {865679437, -1878764039},
2521 {927253046, -1039528932},
2522 {109552593, 1796682209},
2523 {-2092529553, 1230323789},
2524 {691633666, -363689972},
2525 {-2055888649, 1629293099},
2526 {621575320, -41440462},
2527 {-2118132208, 1602646136},
2528 {-1127699509, 1540934679},
2529 {-685265909, -92759291},
2530 {552706886, -1100422738},
2531 {1835797460, -1021495308},
2532 {-892259869, 642482233},
2533 {-1623330855, -465682601},
2534 {-1616524891, 1668858301},
2535 {789892804, 171931394},
2536 {-1289478934, -1583967946},
2537 {-892070738, -2099697608},
2538 {-891243783, -1581843485},
2539 {889398893, -1157596551},
2540 {-1625468793, 1397052267},
2541 {1293049486, 1975934948},
2542 {1369769822, 559147458},
2543 {586484018, 1079279582},
2544 {110115790, 593465182},
2545 {1490499512, 1523195708},
2546 {3552619, 2019042823},
2547 {1471284746, 73681876},
2548 {456448245, -1758372725},
2549 {110547964, 795236088},
2550 {1588977225, -1667224349},
2551 {918540912, -209869746},
2552 {-471874147, 1686524871},
2553 {205978836, 1723747470},
2554 {-1044400758, -129230242},
2555 {-2073307447, -1331212963},
2556 {435230680, 2106561762},
2557 {-864578046, 223891266},
2558 {1660607208, 935692442},
2559 {260214178, -1478445584},
2560 {-1847536522, 198438816},
2561 {-496055048, -967648160},
2562 {-1792066367, 99588025},
2563 {562888306, -956706906},
2564 {-427925529, -1429456799},
2565 {995306285, 1451568081},
2566 {-378017204, -1117626326},
2567 {447918618, -148794216},
2568 {313836902, 1516985844},
2569 {1175970190, -796583652},
2570 {525244071, -148229307},
2571 {-1021702157, -819720157},
2572 {-402507101, 671706427},
2573 {-1556671423, -1183726687},
2574 {61936445, -1614955425},
2575 {112903447, -1779180711},
2576 {1817873886, -1100168350},
2577 {1824679850, -132247350},
2578 {-1628526499, -1863559151},
2579 {547302405, -119235651},
2580 {1840561315, 2114754781},
2581 {-460592212, -1379835144},
2582 {3655341, -151838493},
2583 {1554697726, 418081930},
2584 {-1883959124, 832133926},
2585 {-481416622, 1524187186},
2586 {-481416621, -41896755},
2587 {-481416620, -1607980696},
2588 {-1151126752, 1058261682},
2589 {-1926458555, 794443127}
2590 };
2591
2592 //Languages phrases
2593
2594 private readonly Dictionary<string, Dictionary<string, string>> _messages =
2595 new Dictionary<string, Dictionary<string, string>>
2596 {
2597 {
2598 "FILE_NOT_EXISTS", new Dictionary<string, string>
2599 {
2600 {"en", "File does not exist"},
2601 {"ru", "Файл не существует"},
2602 {"nl", "Bestand bestaat niet."}
2603 }
2604 },
2605 {
2606 "FILE_BROKEN", new Dictionary<string, string>
2607 {
2608 {"en", "Something went wrong during pasting because of a error in the file."},
2609 {"ru", "Файл поврежден, вставка невозможна"},
2610 {"nl", "Er is iets misgegaan tijdens het plakken door een beschadigd bestand."}
2611 }
2612 },
2613 {
2614 "NO_ACCESS", new Dictionary<string, string>
2615 {
2616 {"en", "You don't have the permissions to use this command"},
2617 {"ru", "У вас нет прав доступа к данной команде"},
2618 {"nl", "U heeft geen toestemming/permissie om dit commando te gebruiken."}
2619 }
2620 },
2621 {
2622 "SYNTAX_PASTEBACK", new Dictionary<string, string>
2623 {
2624 {
2625 "en", "Syntax: /pasteback <Target Filename> <options values>\n" +
2626 "height XX - Adjust the height\n" +
2627 "vending - Information and sellings in vending machine\n" +
2628 "stability <true/false> - Wether or not to disable stability\n" +
2629 "deployables <true/false> - Wether or not to copy deployables\n" +
2630 "auth <true/false> - Wether or not to copy lock and cupboard whitelists"
2631 },
2632 {
2633 "ru", "Синтаксис: /pasteback <Название Объекта> <опция значение>\n" +
2634 "height XX - Высота от земли\n" +
2635 "vending - Информация и товары в торговом автомате"
2636 },
2637 {
2638 "nl", "Syntax: /pasteback <Bestandsnaam> <opties waarden>\n" +
2639 "height XX - Pas de hoogte aan \n" +
2640 "vending <true/false> - Informatie en inventaris van \"vending machines\" kopiëren\n" +
2641 "stability <true/false> - of de stabiliteit van het gebouw uitgezet moet worden\n" +
2642 "deployables <true/false> - of de \"deployables\" gekopiërd moeten worden\n" +
2643 "auth <true/false> - Of authorisatie op sloten en tool cupboards gekopiërd moet worden"
2644 }
2645 }
2646 },
2647 {
2648 "SYNTAX_PASTE_OR_PASTEBACK", new Dictionary<string, string>
2649 {
2650 {
2651 "en", "Syntax: /paste or /pasteback <Target Filename> <options values>\n" +
2652 "height XX - Adjust the height\n" +
2653 "autoheight true/false - sets best height, carefull of the steep\n" +
2654 "blockcollision XX - blocks the entire paste if something the new building collides with something\n" +
2655 "deployables true/false - false to remove deployables\n" +
2656 "inventories true/false - false to ignore inventories\n" +
2657 "vending - Information and sellings in vending machine\n" +
2658 "stability <true/false> - Wether or not to disable stability on the building"
2659 },
2660 {
2661 "ru", "Синтаксис: /paste or /pasteback <Название Объекта> <опция значение>\n" +
2662 "height XX - Высота от земли\n" +
2663 "autoheight true/false - автоматически подобрать высоту от земли\n" +
2664 "blockcollision XX - блокировать вставку, если что-то этому мешает\n" +
2665 "deployables true/false - false для удаления предметов\n" +
2666 "inventories true/false - false для игнорирования копирования инвентаря\n" +
2667 "vending - Информация и товары в торговом автомате"
2668 },
2669 {
2670 "nl", "Syntax: /paste of /pasteback <Bestandsnaam> <opties waarden>\n" +
2671 "height XX - Pas de hoogte aan \n" +
2672 "autoheight true/false - probeert de optimale hoogte te vinden om gebouw te plaatsen. Werkt optimaal op vlakke grond.\n" +
2673 "vending true/false - Informatie en inventaris van \"vending machines\" kopiëren\n" +
2674 "stability <true/false> - of de stabiliteit van het gebouw uitgezet moet worden\n" +
2675 "deployables <true/false> - of de \"deployables\" gekopiërd moeten worden\n" +
2676 "auth <true/false> - Of authorisatie op sloten en tool cupboards gekopiërd moet worden"
2677 }
2678 }
2679 },
2680 {
2681 "PASTEBACK_SUCCESS", new Dictionary<string, string>
2682 {
2683 {"en", "You've successfully placed back the structure"},
2684 {"ru", "Постройка успешно вставлена на старое место"},
2685 {"nl", "Het gebouw is succesvol teruggeplaatst."}
2686 }
2687 },
2688 {
2689 "PASTE_SUCCESS", new Dictionary<string, string>
2690 {
2691 {"en", "You've successfully pasted the structure"},
2692 {"ru", "Постройка успешно вставлена"},
2693 {"nl", "Het gebouw is succesvol geplaatst."}
2694 }
2695 },
2696 {
2697 "SYNTAX_COPY", new Dictionary<string, string>
2698 {
2699 {
2700 "en", "Syntax: /copy <Target Filename> <options values>\n" +
2701 "radius XX (default 3) - The radius in which to search for the next object (performs this search from every other object)\n" +
2702 "method proximity/building (default proximity) - Building only copies objects which are part of the building, proximity copies everything (within the radius)\n" +
2703 "deployables true/false (saves deployables or not) - Wether to save deployables\n" +
2704 "inventories true/false (saves inventories or not) - Wether to save inventories of found objects with inventories."
2705 },
2706 {
2707 "ru", "Синтаксис: /copy <Название Объекта> <опция значение>\n" +
2708 "radius XX (default 3)\n" +
2709 "method proximity/building (по умолчанию proximity)\n" +
2710 "deployables true/false (сохранять предметы или нет)\n" +
2711 "inventories true/false (сохранять инвентарь или нет)"
2712 },
2713 {
2714 "nl", "Syntax: /copy <Bestandsnaam> <opties waarden>\n" +
2715 "radius XX (standaard 3) - De radius waarin copy paste naar het volgende object zoekt\n" +
2716 "method proximity/building (standaard proximity) - Building kopieërd alleen objecten die bij het gebouw horen, proximity kopieërd alles wat gevonden is\n" +
2717 "deployables true/false (saves deployables or not) - Of de data van gevonden \"deployables\" opgeslagen moet worden\n" +
2718 "inventories true/false (saves inventories or not) - Of inventarissen van objecten (kisten, tool cupboards, etc) opgeslagen moet worden"
2719 }
2720 }
2721 },
2722 {
2723 "NO_ENTITY_RAY", new Dictionary<string, string>
2724 {
2725 {"en", "Couldn't ray something valid in front of you"},
2726 {"ru", "Не удалось найти какой-либо объект перед вами"},
2727 {"nl", "U kijkt niet naar een geschikt object om een kopie op te starten."}
2728 }
2729 },
2730 {
2731 "COPY_SUCCESS", new Dictionary<string, string>
2732 {
2733 {"en", "The structure was successfully copied as {0}"},
2734 {"ru", "Постройка успешно скопирована под названием: {0}"},
2735 {"nl", "Gebouw is succesvol gekopieërd"}
2736 }
2737 },
2738 {
2739 "NO_PASTED_STRUCTURE", new Dictionary<string, string>
2740 {
2741 {"en", "You must paste structure before undoing it"},
2742 {"ru", "Вы должны вставить постройку перед тем, как отменить действие"},
2743 {"nl", "U moet eerst een gebouw terugplaatsen alvorens deze ongedaan gemaakt kan worden (duhh)"}
2744 }
2745 },
2746 {
2747 "UNDO_SUCCESS", new Dictionary<string, string>
2748 {
2749 {"en", "You've successfully undid what you pasted"},
2750 {"ru", "Вы успешно снесли вставленную постройку"},
2751 {"nl", "Laatse geplaatste gebouw is succesvol ongedaan gemaakt."}
2752 }
2753 },
2754 {
2755 "NOT_FOUND_PLAYER", new Dictionary<string, string>
2756 {
2757 {"en", "Couldn't find the player"},
2758 {"ru", "Не удалось найти игрока"},
2759 {"nl", "Speler niet gevonden."}
2760 }
2761 },
2762 {
2763 "SYNTAX_BOOL", new Dictionary<string, string>
2764 {
2765 {"en", "Option {0} must be true/false"},
2766 {"ru", "Опция {0} принимает значения true/false"},
2767 {"nl", "Optie {0} moet true of false zijn"}
2768 }
2769 },
2770 {
2771 "SYNTAX_HEIGHT", new Dictionary<string, string>
2772 {
2773 {"en", "Option height must be a number"},
2774 {"ru", "Опция height принимает только числовые значения"},
2775 {"nl", "De optie height accepteert alleen nummers"}
2776 }
2777 },
2778 {
2779 "SYNTAX_BLOCKCOLLISION", new Dictionary<string, string>
2780 {
2781 {"en", "Option blockcollision must be a number, 0 will deactivate the option"},
2782 {
2783 "ru",
2784 "Опция blockcollision принимает только числовые значения, 0 позволяет отключить проверку"
2785 },
2786 {"nl", "Optie blockcollision accepteert alleen nummers, 0 schakelt deze functionaliteit uit"}
2787 }
2788 },
2789 {
2790 "SYNTAX_RADIUS", new Dictionary<string, string>
2791 {
2792 {"en", "Option radius must be a number"},
2793 {"ru", "Опция radius принимает только числовые значения"},
2794 {"nl", "Optie height accepteert alleen nummers"}
2795 }
2796 },
2797 {
2798 "BLOCKING_PASTE", new Dictionary<string, string>
2799 {
2800 {"en", "Something is blocking the paste"},
2801 {"ru", "Что-то препятствует вставке"},
2802 {"nl", "Iets blokkeert het plaatsen van dit gebouw"}
2803 }
2804 },
2805 {
2806 "AVAILABLE_STRUCTURES", new Dictionary<string, string>
2807 {
2808 {"ru", "<color=orange>Доступные постройки:</color>"},
2809 {"en", "<color=orange>Available structures:</color>"},
2810 {"nl", "Beschikbare bestanden om te plaatsen zijn:"}
2811 }
2812 }
2813 };
2814
2815 public class CopyData
2816 {
2817 public BasePlayer Player;
2818 public Stack<Vector3> CheckFrom = new Stack<Vector3>();
2819 public HashSet<BaseEntity> HouseList = new HashSet<BaseEntity>();
2820 public List<object> RawData = new List<object>();
2821 public Vector3 SourcePos;
2822 public Vector3 SourceRot;
2823 public Action Callback;
2824
2825 public string FileName;
2826 public int CurrentLayer;
2827 public float RotCor;
2828 public float Range;
2829 public bool SaveTree;
2830 public bool SaveShare;
2831 public CopyMechanics CopyMechanics;
2832 public bool EachToEach;
2833 public uint BuildingId = 0;
2834
2835#if DEBUG
2836 public Stopwatch Sw = new Stopwatch();
2837#endif
2838 }
2839
2840 public class PasteData
2841 {
2842 public ICollection<Dictionary<string, object>> Entities;
2843 public List<BaseEntity> PastedEntities = new List<BaseEntity>();
2844
2845 public Dictionary<uint, Dictionary<string, object>> IoEntities =
2846 new Dictionary<uint, Dictionary<string, object>>();
2847
2848 public BasePlayer Player;
2849 public List<StabilityEntity> StabilityEntities = new List<StabilityEntity>();
2850 public Quaternion QuaternionRotation;
2851 public Action Callback;
2852
2853 public bool Auth;
2854 public Vector3 StartPos;
2855 public float HeightAdj;
2856 public bool Stability;
2857 public bool IsItemReplace;
2858 public bool Ownership;
2859
2860 public uint BuildingId = 0;
2861
2862#if DEBUG
2863 public Stopwatch Sw = new Stopwatch();
2864#endif
2865 }
2866 }
2867}