C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Reference: System.Drawing
//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 Facepunch;
using Graphics = System.Drawing.Graphics;
using ImageFormat = System.Drawing.Imaging.ImageFormat;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using UnityEngine;
using Debug = System.Diagnostics.Debug;
namespace Oxide.Plugins
{
[Info("Copy Paste", "Reneb & MiRror & Misstake", "4.1.14", ResourceId = 716)]
[Description("Copy and paste buildings to save them or move them")]
public class CopyPaste : RustPlugin
{
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"
, serverID = "Server"
, subDirectory = "copypaste/";
private Dictionary<string, Stack<List<BaseEntity>>> lastPastes = new Dictionary<string, Stack<List<BaseEntity>>>();
private Dictionary<string, SignSize> signSizes = new Dictionary<string, SignSize>()
{
//{"spinner.wheel.deployed", new SignSize(512, 512)},
{"sign.pictureframe.landscape", new SignSize(256, 128)},
{"sign.pictureframe.tall", new SignSize(128, 512)},
{"sign.pictureframe.portrait", new SignSize(128, 256)},
{"sign.pictureframe.xxl", new SignSize(1024, 512)},
{"sign.pictureframe.xl", new SignSize(512, 512)},
{"sign.small.wood", new SignSize(128, 64)},
{"sign.medium.wood", new SignSize(256, 128)},
{"sign.large.wood", new SignSize(256, 128)},
{"sign.huge.wood", new SignSize(512, 128)},
{"sign.hanging.banner.large", new SignSize(64, 256)},
{"sign.pole.banner.large", new SignSize(64, 256)},
{"sign.post.single", new SignSize(128, 64)},
{"sign.post.double", new SignSize(256, 256)},
{"sign.post.town", new SignSize(256, 128)},
{"sign.post.town.roof", new SignSize(256, 128)},
{"sign.hanging", new SignSize(128, 256)},
{"sign.hanging.ornate", new SignSize(256, 128)},
};
private List<BaseEntity.Slot> checkSlots = new List<BaseEntity.Slot>()
{
BaseEntity.Slot.Lock,
BaseEntity.Slot.UpperModifier,
BaseEntity.Slot.MiddleModifier,
BaseEntity.Slot.LowerModifier
};
public enum CopyMechanics { Building, Proximity }
private class SignSize
{
public int width;
public int height;
public SignSize(int width, int height)
{
this.width = width;
this.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<ConfigData>();
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);
Dictionary<string, Dictionary<string, string>> compiledLangs = new Dictionary<string, Dictionary<string, string>>();
foreach (var line in messages)
{
foreach (var translate in line.Value)
{
if (!compiledLangs.ContainsKey(translate.Key))
compiledLangs[translate.Key] = new Dictionary<string, string>();
compiledLangs[translate.Key][line.Key] = translate.Value;
}
}
foreach (var cLangs in compiledLangs)
{
lang.RegisterMessages(cLangs.Value, this, cLangs.Key);
}
}
private void OnServerInitialized()
{
LoadVariables();
Vis.colBuffer = new Collider[8192 * 16];
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
}
#region API
private object TryCopyFromSteamID(ulong userID, string filename, string[] args, Action callback = null)
{
var player = BasePlayer.FindByID(userID);
if (player == null)
return Lang("NOT_FOUND_PLAYER", userID.ToString());
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, rayCopy))
return Lang("NO_ENTITY_RAY", player.UserIDString);
return TryCopy(hit.point, hit.GetEntity().GetNetworkRotation().eulerAngles, filename, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player, callback);
}
private object TryPasteFromSteamID(ulong userID, string filename, string[] args, Action callback = null)
{
var player = BasePlayer.FindByID(userID);
if (player == null)
return Lang("NOT_FOUND_PLAYER", player.UserIDString);
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, DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, callback: callback);
}
private object TryPasteFromVector3(Vector3 pos, float rotationCorrection, string filename, string[] args, Action callback = null)
{
return TryPaste(pos, filename, null, rotationCorrection, args, callback: callback);
}
#endregion
//Other methods
private object CheckCollision(HashSet<Dictionary<string, object>> entities, Vector3 startPos, float radius)
{
foreach (var entityobj in entities)
{
if (Physics.CheckSphere((Vector3)entityobj["position"], radius, copyLayer))
return Lang("BLOCKING_PASTE", null);
}
return true;
}
private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot)
{
List<BaseEntity> ents = new List<BaseEntity>();
Vis.Entities<BaseEntity>(pos, 2f, ents);
foreach (BaseEntity ent in ents)
{
if (ent.PrefabName == prefabname && ent.transform.position == pos && ent.transform.rotation == rot)
return true;
}
return false;
}
private object cmdPasteBack(BasePlayer player, string[] args)
{
string userIDString = (player == null) ? serverID : player.UserIDString;
if (args.Length < 1)
return Lang("SYNTAX_PASTEBACK", userIDString);
var success = TryPasteBack(args[0], player, args.Skip(1).ToArray());
if (success is string)
return (string)success;
return true;
}
private object cmdUndo(string userIDString, string[] args)
{
var player = BasePlayer.Find(userIDString);
if (!lastPastes.ContainsKey(userIDString))
return Lang("NO_PASTED_STRUCTURE", userIDString);
HashSet<BaseEntity> entities = new HashSet<BaseEntity>(lastPastes[userIDString].Pop().ToList());
UndoLoop(entities, player);
return true;
}
private void UndoLoop(HashSet<BaseEntity> entities, BasePlayer player, int count = 0)
{
foreach (var storageContainer in entities.OfType<StorageContainer>())
{
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();
}
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)
{
if (player != null)
SendReply(player, "Undo cancelled because of infinite loop.");
else
Puts("Undo cancelled because of infinite loop.");
return;
}
if(entities.Count > 0)
NextTick(() => UndoLoop(entities, player, entities.Count));
else
{
if (player != null)
SendReply(player, Lang("UNDO_SUCCESS", player.UserIDString));
else
Puts(Lang("UNDO_SUCCESS"));
if (lastPastes[player?.UserIDString ?? serverID].Count == 0)
lastPastes.Remove(player?.UserIDString ?? serverID);
}
}
private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection, CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, BasePlayer player, Action callback)
{
int 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,
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(int i = 0; i < batchSize; i++)
{
if (checkFrom.Count == 0)
break;
List<BaseEntity> list = Pool.GetList<BaseEntity>();
Vis.Entities<BaseEntity>(checkFrom.Pop(), copyData.Range, list, copyData.CurrentLayer);
foreach (var entity in list)
{
if (!houseList.Add(entity))
continue;
if (copyMechanics == CopyMechanics.Building)
{
BuildingBlock buildingBlock = entity.GetComponentInParent<BuildingBlock>();
if (buildingBlock != null)
{
if (buildingID == 0)
buildingID = buildingBlock.buildingID;
if (buildingID != buildingBlock.buildingID)
continue;
}
}
if (copyData.EachToEach)
checkFrom.Push(entity.transform.position);
if (entity.GetComponent<BaseLock>() != null)
continue;
copyData.RawData.Add(EntityData(entity, entity.transform.position, entity.transform.rotation.eulerAngles / 57.29578f, copyData));
}
copyData.BuildingID = buildingID;
}
if (checkFrom.Count > 0)
{
NextTick(() => CopyLoop(copyData));
}
else
{
string path = subDirectory + copyData.FileName;
var datafile = Interface.Oxide.DataFileSystem.GetDatafile(path);
datafile.Clear();
var sourcePos = copyData.SourcePos;
datafile["default"] = new Dictionary<string, object>
{
{"position", new Dictionary<string, object>
{
{"x", sourcePos.x.ToString()},
{"y", sourcePos.y.ToString()},
{"z", sourcePos.z.ToString()}
}
},
{"rotationy", copyData.SourceRot.y.ToString()},
{"rotationdiff", copyData.RotCor.ToString()}
};
datafile["entities"] = copyData.RawData;
datafile["protocol"] = new Dictionary<string, object>
{
{"items", 2},
{"version", Version}
};
Interface.Oxide.DataFileSystem.SaveDatafile(path);
SendReply(copyData.Player, Lang("COPY_SUCCESS", copyData.Player.UserIDString, copyData.FileName));
copyData.callback?.Invoke();
Interface.CallHook("OnCopyFinished", copyData.RawData);
}
}
private float DegreeToRadian(float angle)
{
return (float)(Math.PI * angle / 180.0f);
}
private Dictionary<string, object> EntityData(BaseEntity entity, Vector3 entPos, Vector3 entRot, CopyData copyData)
{
var normalizedPos = NormalizePosition(copyData.SourcePos, entPos, copyData.RotCor);
entRot.y -= copyData.RotCor;
var data = new Dictionary<string, object>
{
{"prefabname", entity.PrefabName},
{"skinid", entity.skinID},
{"flags", TryCopyFlags(entity)},
{"pos", new Dictionary<string,object>
{
{"x", normalizedPos.x.ToString()},
{"y", normalizedPos.y.ToString()},
{"z", normalizedPos.z.ToString()}
}
},
{"rot", new Dictionary<string,object>
{
{"x", entRot.x.ToString()},
{"y", entRot.y.ToString()},
{"z", entRot.z.ToString()},
}
},
{"ownerid", entity.OwnerID }
};
TryCopySlots(entity, data, copyData.SaveShare);
var buildingblock = entity as BuildingBlock;
if (buildingblock != null)
{
data.Add("grade", buildingblock.grade);
}
var box = entity as StorageContainer;
if (box != null)
{
var itemlist = new List<object>();
foreach (Item item in box.inventory.itemList)
{
var itemdata = new Dictionary<string, object>
{
{ "condition", item.condition.ToString() },
{ "id", item.info.itemid },
{ "amount", item.amount },
{ "skinid", item.skin },
{ "position", item.position },
{ "blueprintTarget", item.blueprintTarget },
};
if (!string.IsNullOrEmpty(item.text))
itemdata["text"] = item.text;
var heldEnt = item.GetHeldEntity();
if (heldEnt != null)
{
var projectiles = heldEnt.GetComponent<BaseProjectile>();
if (projectiles != null)
{
var magazine = projectiles.primaryMagazine;
if (magazine != null)
{
itemdata.Add("magazine", new Dictionary<string, object>
{
{ magazine.ammoType.itemid.ToString(), magazine.contents }
});
}
}
}
if (item?.contents?.itemList != null)
{
var contents = new List<object>();
foreach (Item itemContains in item.contents.itemList)
{
contents.Add(new Dictionary<string, object>
{
{"id", itemContains.info.itemid },
{"amount", itemContains.amount },
});
}
itemdata["items"] = contents;
}
itemlist.Add(itemdata);
}
data.Add("items", itemlist);
}
var sign = entity as Signage;
if (sign != null)
{
var imageByte = FileStorage.server.Get(sign.textureID, FileStorage.Type.png, sign.net.ID);
data.Add("sign", new Dictionary<string, object>
{
{"locked", sign.IsLocked()}
});
if (sign.textureID > 0 && imageByte != null)
((Dictionary<string, object>)data["sign"]).Add("texture", Convert.ToBase64String(imageByte));
}
if (copyData.SaveShare)
{
var sleepingBag = entity as SleepingBag;
if (sleepingBag != null)
{
data.Add("sleepingbag", new Dictionary<string, object>
{
{"niceName", sleepingBag.niceName },
{"deployerUserID", sleepingBag.deployerUserID },
{"isPublic", sleepingBag.IsPublic() },
});
}
var cupboard = entity as BuildingPrivlidge;
if (cupboard != null)
{
data.Add("cupboard", new Dictionary<string, object>
{
{"authorizedPlayers", cupboard.authorizedPlayers.Select(y => y.userid).ToList() }
});
}
var autoTurret = entity as AutoTurret;
if (autoTurret != null)
{
data.Add("autoturret", new Dictionary<string, object>
{
{"authorizedPlayers", autoTurret.authorizedPlayers.Select(p => p.userid).ToList() }
});
}
}
var vendingMachine = entity as VendingMachine;
if (vendingMachine != null)
{
var sellOrders = new List<object>();
foreach (var vendItem in vendingMachine.sellOrders.sellOrders)
{
sellOrders.Add(new Dictionary<string, object>
{
{ "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<string, object>
{
{"shopName", vendingMachine.shopName },
{"isBroadcasting", vendingMachine.IsBroadcasting() },
{"sellOrders", sellOrders}
});
}
var ioEntity = entity as IOEntity;
if (ioEntity != null)
{
var ioData = new Dictionary<string, object>();
List<object> inputs = ioEntity.inputs.Select(input => new Dictionary<string, object>
{
{"connectedID", input.connectedTo.entityRef.uid},
{"connectedToSlot", input.connectedToSlot},
{"niceName", input.niceName},
{"type", (int) input.type},
}).Cast<object>().ToList();
ioData.Add("inputs", inputs);
List<object> outputs = new List<object>();
foreach (IOEntity.IOSlot output in ioEntity.outputs)
{
Dictionary<string, object> ioConnection = new Dictionary<string, object>
{
{"connectedID", output.connectedTo.entityRef.uid },
{"connectedToSlot", output.connectedToSlot },
{"niceName", output.niceName },
{"type", (int)output.type },
};
var linePoints = new List<Vector3>();
if (output.linePoints != null)
{
// Since Vector3 is struct should be able to simply save the Vecot directly.foreach (Vector3 linePoint in output.linePoints)
{
linePoints.Add(NormalizePosition(copyData.SourcePos, linePoint, copyData.RotCor));
}
}
ioConnection.Add("linePoints", linePoints);
outputs.Add(ioConnection);
}
ioData.Add("outputs", outputs);
ioData.Add("oldID", ioEntity.net.ID);
var electricalBranch = ioEntity as ElectricalBranch;
if (electricalBranch != null)
{
ioData.Add("branchAmount", electricalBranch.branchAmount);
}
/*var counter = ioEntity.GetComponent<PowerCounter>();
if (counter != null)
{
ioData.Add("targetNumber", counter.GetTarget());
}*/
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());
}
data.Add("IOEntity", ioData);
}
return data;
}
private object FindBestHeight(HashSet<Dictionary<string, object>> entities, Vector3 startPos)
{
float maxHeight = 0f;
foreach (var entity in entities)
{
if (((string)entity["prefabname"]).Contains("/foundation/"))
{
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 void FixSignage(Signage sign, byte[] imageBytes)
{
if (!signSizes.ContainsKey(sign.ShortPrefabName))
return;
byte[] resizedImage = ImageResize(imageBytes, signSizes[sign.ShortPrefabName].width, signSizes[sign.ShortPrefabName].height);
sign.textureID = FileStorage.server.Store(resizedImage, FileStorage.Type.png, sign.net.ID);
}
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(BasePlayer player, string permName)
{
return player.IsAdmin || permission.UserHasPermission(player.UserIDString, 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);
MemoryStream 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)System.Math.Cos(-diffRot)) + (transformedPos.z * (float)System.Math.Sin(-diffRot));
var newZ = (transformedPos.z * (float)System.Math.Cos(-diffRot)) - (transformedPos.x * (float)System.Math.Sin(-diffRot));
transformedPos.x = newX;
transformedPos.z = newZ;
return transformedPos;
}
private void Paste(ICollection<Dictionary<string, object>> entities, Dictionary<string, object> protocol, Vector3 startPos, BasePlayer player, bool stability, float RotationCorrection, float heightAdj, bool auth, Action callback)
{
var ioEntities = new Dictionary<uint, Dictionary<string, object>>();
uint buildingID = 0;
//Settings
bool isItemReplace = !protocol.ContainsKey("items");
var eulerRotation = new Vector3(0f, RotationCorrection * 57.2958f, 0f);
var quaternionRotation = Quaternion.Euler(eulerRotation);
var pasteData = new PasteData()
{
HeightAdj = heightAdj,
IsItemReplace = isItemReplace,
Entities = entities,
Player = player,
QuaternionRotation = quaternionRotation,
StartPos = startPos,
Stability = stability,
Auth = auth,
callback = callback
};
NextTick(() => PasteLoop(pasteData));
}
private void PasteLoop(PasteData pasteData)
{
var entities = pasteData.Entities;
var todo = entities.Take(config.PasteBatchSize).ToArray();
var player = pasteData.Player;
foreach (var data in todo)
{
entities.Remove(data);
var prefabname = (string)data["prefabname"];
ulong skinid = ulong.Parse(data["skinid"].ToString());
var pos = (Vector3)data["position"];
var rot = (Quaternion)data["rotation"];
ulong ownerId = player?.userID ?? 0;
if (data.ContainsKey("ownerid"))
{
ownerId = Convert.ToUInt64(data["ownerid"]);
}
if (CheckPlaced(prefabname, pos, rot))
continue;
if (prefabname.Contains("pillar"))
continue;
// 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"))
continue;
var entity = GameManager.server.CreateEntity(prefabname, pos, rot, true);
if (entity == null)
continue;
entity.transform.position = pos;
entity.transform.rotation = rot;
if (player != null)
entity.SendMessage("SetDeployedBy", player, SendMessageOptions.DontRequireReceiver);
entity.OwnerID = ownerId;
BuildingBlock buildingBlock = entity as BuildingBlock;
if (buildingBlock != null)
{
buildingBlock.blockDefinition = PrefabAttribute.server.Find<Construction>(buildingBlock.prefabID);
buildingBlock.SetGrade((BuildingGrade.Enum)data["grade"]);
if(!pasteData.Stability)
buildingBlock.grounded = true;
}
DecayEntity decayEntity = entity as DecayEntity;
if (decayEntity != null)
{
if (pasteData.BuildingID == 0)
pasteData.BuildingID = BuildingManager.server.NewBuildingID();
decayEntity.AttachToBuilding(pasteData.BuildingID);
}
StabilityEntity stabilityEntity = entity as StabilityEntity;
if (stabilityEntity != null)
{
if (!stabilityEntity.grounded)
{
stabilityEntity.grounded = true;
pasteData.StabilityEntities.Add(stabilityEntity);
}
}
entity.skinID = skinid;
entity.Spawn();
var baseCombat = entity as BaseCombatEntity;
if (baseCombat != null)
baseCombat.ChangeHealth(baseCombat.MaxHealth());
pasteData.PastedEntities.AddRange(TryPasteSlots(entity, data, pasteData));
var box = entity as StorageContainer;
if (box != null)
{
Locker locker = box as Locker;
if (locker != null)
locker.equippingActive = true;
box.inventory.Clear();
var items = new List<object>();
if (data.ContainsKey("items"))
items = data["items"] as List<object>;
foreach (var itemDef in items)
{
var item = itemDef as Dictionary<string, object>;
var itemid = Convert.ToInt32(item["id"]);
var itemamount = Convert.ToInt32(item["amount"]);
var itemskin = ulong.Parse(item["skinid"].ToString());
var itemcondition = Convert.ToSingle(item["condition"]);
if (pasteData.IsItemReplace)
itemid = GetItemID(itemid);
var i = ItemManager.CreateByItemID(itemid, itemamount, itemskin);
if (i != null)
{
i.condition = itemcondition;
if (item.ContainsKey("text"))
i.text = item["text"].ToString();
if (item.ContainsKey("blueprintTarget"))
{
int blueprintTarget = Convert.ToInt32(item["blueprintTarget"]);
if (pasteData.IsItemReplace)
blueprintTarget = GetItemID(blueprintTarget);
i.blueprintTarget = blueprintTarget;
}
if (item.ContainsKey("magazine"))
{
var heldent = i.GetHeldEntity();
if (heldent != null)
{
var projectiles = heldent.GetComponent<BaseProjectile>();
if (projectiles != null)
{
var magazine = item["magazine"] as Dictionary<string, object>;
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;
}
//TODO Doesn't add water to some containers
if (item.ContainsKey("items"))
{
var itemContainsList = item["items"] as List<object>;
foreach (var itemContains in itemContainsList)
{
var contents = itemContains as Dictionary<string, object>;
int contentsItemID = Convert.ToInt32(contents["id"]);
if (pasteData.IsItemReplace)
contentsItemID = GetItemID(contentsItemID);
i.contents.AddItem(ItemManager.FindItemDefinition(contentsItemID), Convert.ToInt32(contents["amount"]));
}
}
}
}
int targetPos = -1;
if (item.ContainsKey("position"))
targetPos = Convert.ToInt32(item["position"]);
i.MoveToContainer(box.inventory, targetPos);
}
}
if (locker != null)
locker.equippingActive = false;
}
var sign = entity as Signage;
if (sign != null && data.ContainsKey("sign"))
{
var signData = data["sign"] as Dictionary<string, object>;
if (signData.ContainsKey("texture"))
{
byte[] imageBytes = Convert.FromBase64String(signData["texture"].ToString());
FixSignage(sign, imageBytes);
}
if (Convert.ToBoolean(signData["locked"]))
sign.SetFlag(BaseEntity.Flags.Locked, true);
sign.SendNetworkUpdate();
}
var sleepingBag = entity as SleepingBag;
if (sleepingBag != null && data.ContainsKey("sleepingbag"))
{
var bagData = data["sleepingbag"] as Dictionary<string, object>;
sleepingBag.niceName = bagData["niceName"].ToString();
sleepingBag.deployerUserID = ulong.Parse(bagData["deployerUserID"].ToString());
sleepingBag.SetPublic(Convert.ToBoolean(bagData["isPublic"]));
}
var autoturret = entity as AutoTurret;
if (autoturret != null)
{
List<ulong> authorizedPlayers = new List<ulong>();
if (data.ContainsKey("autoturret"))
{
var autoTurretData = data["autoturret"] as Dictionary<string, object>;
authorizedPlayers = (autoTurretData["authorizedPlayers"] as List<object>).Select(Convert.ToUInt64).ToList();
}
if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth)
authorizedPlayers.Add(player.userID);
foreach (var userID in authorizedPlayers)
{
autoturret.authorizedPlayers.Add(new PlayerNameID()
{
userid = Convert.ToUInt64(userID),
username = "Player"
});
}
autoturret.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
var cupboard = entity as BuildingPrivlidge;
if (cupboard != null)
{
List<ulong> authorizedPlayers = new List<ulong>();
if (data.ContainsKey("cupboard"))
{
var cupboardData = data["cupboard"] as Dictionary<string, object>;
authorizedPlayers = (cupboardData["authorizedPlayers"] as List<object>).Select(Convert.ToUInt64).ToList();
}
if (player != null && !authorizedPlayers.Contains(player.userID) && pasteData.Auth)
authorizedPlayers.Add(player.userID);
foreach (var userID in authorizedPlayers)
{
cupboard.authorizedPlayers.Add(new PlayerNameID()
{
userid = Convert.ToUInt64(userID),
username = "Player"
});
}
cupboard.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
var vendingMachine = entity as VendingMachine;
if (vendingMachine != null && data.ContainsKey("vendingmachine"))
{
var vendingData = data["vendingmachine"] as Dictionary<string, object>;
vendingMachine.shopName = vendingData["shopName"].ToString();
vendingMachine.SetFlag(BaseEntity.Flags.Reserved4, Convert.ToBoolean(vendingData["isBroadcasting"]));
var sellOrders = vendingData["sellOrders"] as List<object>;
foreach (var orderPreInfo in sellOrders)
{
var orderInfo = orderPreInfo as Dictionary<string, object>;
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 != null)
{
var ioData = new Dictionary<string, object>();
if (data.ContainsKey("IOEntity"))
{
ioData = data["IOEntity"] as Dictionary<string, object> ?? new Dictionary<string, object>();
}
ioData.Add("entity", ioEntity);
ioData.Add("newId", ioEntity.net.ID);
object oldIdObject = 0;
if (ioData.TryGetValue("oldID", out oldIdObject))
{
var oldId = Convert.ToUInt32(oldIdObject);
pasteData.IoEntities.Add(oldId, ioData);
}
}
var flagsData = new Dictionary<string, object>();
if (data.ContainsKey("flags"))
flagsData = data["flags"] as Dictionary<string, object>;
var flags = new Dictionary<BaseEntity.Flags, bool>();
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);
}
pasteData.PastedEntities.Add(entity);
}
if (entities.Count > 0)
NextTick(() => PasteLoop(pasteData));
else
{
foreach (var ioData in pasteData.IoEntities.Values.ToArray())
{
if (!ioData.ContainsKey("entity"))
continue;
var ioEntity = ioData["entity"] as IOEntity;
List<object> inputs = null;
if (ioData.ContainsKey("inputs"))
inputs = ioData["inputs"] as List<object>;
var electricalBranch = ioEntity as ElectricalBranch;
if (electricalBranch != null && ioData.ContainsKey("branchAmount"))
{
electricalBranch.branchAmount = Convert.ToInt32(ioData["branchAmount"]);
}
// Realized counter.targetCounterNumber is private, leaving it in in cas
// ... [Code preview truncated due to length. Full source in attached file] ...