C#:
//If debug is defined it will add a stopwatch to the paste and copydata which can be used to profile copying and pasting.//#define DEBUG
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using ProtoBuf;
using UnityEngine;
using Graphics = System.Drawing.Graphics;
/*
* CREDITS
*
* Orange - Saving ContainerIOEntity
* UIP88 - Turrets fix
* bsdinis - Wire fix
* nivex - Ownership option, sign fix
* DezLife - CCTV fix
*
*/
namespace Oxide.Plugins
{
[Info("Copy Paste", "misticos", "4.1.27")] // Wulf skipped 24 :(
[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)},
{"sign.neon.xl.animated", new SignSize(250, 250)},
{"sign.neon.xl", new SignSize(250, 250)},
{"sign.neon.125x215.animated", new SignSize(215, 125)},
{"sign.neon.125x215", new SignSize(215, 125)},
{"sign.neon.125x125", new SignSize(125, 125)},
};
private List<BaseEntity.Slot> _checkSlots = new List<BaseEntity.Slot>
{
BaseEntity.Slot.Lock,
BaseEntity.Slot.UpperModifier,
BaseEntity.Slot.MiddleModifier,
BaseEntity.Slot.LowerModifier
};
public enum CopyMechanics
{
Building,
Proximity
}
private class SignSize
{
public int Width;
public int Height;
public SignSize(int width, int height)
{
Width = width;
Height = height;
}
}
//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);
var compiledLangs = new Dictionary<string, Dictionary<string, string>>();
foreach (var line in _messages)
{
foreach (var translate in line.Value)
{
if (!compiledLangs.ContainsKey(translate.Key))
compiledLangs[translate.Key] = new Dictionary<string, string>();
compiledLangs[translate.Key][line.Key] = translate.Value;
}
}
foreach (var cLangs in compiledLangs)
{
lang.RegisterMessages(cLangs.Value, this, cLangs.Key);
}
}
private void OnServerInitialized()
{
LoadVariables();
Vis.colBuffer = new Collider[8192 * 16];
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
}
#region API
private object TryCopyFromSteamId(ulong userId, string filename, string[] args, Action callback = null)
{
var player = 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");
}
return true;
}
private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot)
{
const float maxDiff = 0.01f;
var ents = new List<BaseEntity>();
Vis.Entities(pos, maxDiff, ents);
foreach (var ent in ents)
{
if (ent.PrefabName != prefabname)
continue;
if (Vector3.Distance(ent.transform.position, pos) > maxDiff)
{
continue;
}
if (Vector3.Distance(ent.transform.rotation.eulerAngles, rot.eulerAngles) > maxDiff)
{
continue;
}
return true;
}
return false;
}
private object CmdPasteBack(BasePlayer player, string[] args)
{
var 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);
var 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>().Where(x => !x.IsDestroyed))
{
storageContainer.Kill();
}
// Take an amount of entities from the entity list (defined in config) and kill them.Will be repeated for every tick until there are no entities left.entities.Take(_config.UndoBatchSize).ToList().ForEach(p =>
{
entities.Remove(p);
// Cleanup the hotspot beloning to the node.var ore = p as OreResourceEntity;
if (ore != null)
{
ore.CleanupBonus();
}
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)
{
var currentLayer = _copyLayer;
if (saveTree)
currentLayer |= LayerMask.GetMask("Tree");
var copyData = new CopyData
{
Filename = filename,
CurrentLayer = currentLayer,
RotCor = rotationCorrection,
Range = range,
SaveShare = saveShare,
SaveTree = saveTree,
CopyMechanics = copyMechanics,
EachToEach = eachToEach,
SourcePos = sourcePos,
SourceRot = sourceRot,
Player = player,
Callback = callback
};
copyData.CheckFrom.Push(sourcePos);
NextTick(() => CopyLoop(copyData));
;
}
// Main loop for copy, will fetch all the data needed.Is called every tick untill copy is done (can't find any entities)
private void CopyLoop(CopyData copyData)
{
var checkFrom = copyData.CheckFrom;
var houseList = copyData.HouseList;
var buildingId = copyData.BuildingId;
var copyMechanics = copyData.CopyMechanics;
var batchSize = checkFrom.Count < _config.CopyBatchSize ? checkFrom.Count : _config.CopyBatchSize;
for (var i = 0; i < batchSize; i++)
{
if (checkFrom.Count == 0)
break;
var list = Pool.GetList<BaseEntity>();
Vis.Entities(checkFrom.Pop(), copyData.Range, list, copyData.CurrentLayer);
foreach (var entity in list)
{
if (!houseList.Add(entity))
continue;
if (copyMechanics == CopyMechanics.Building)
{
var 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
{
var 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, copyData.Filename);
}
}
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?.inventory != null)
{
var itemlist = new List<object>();
foreach (var 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 (var 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 box2 = entity as ContainerIOEntity;
if (box2 != null)
{
var itemlist = new List<object>();
foreach (var item in box2.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 (var 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 && sign.textureIDs != null)
{
data.Add("sign", new Dictionary<string, object>
{
{"locked", sign.IsLocked()}
});
var signData = (Dictionary<string, object>) data["sign"];
for (int num = 0; num < sign.textureIDs.Length; num++)
{
var textureId = sign.textureIDs[num];
if (textureId == 0)
continue;
var imageByte = FileStorage.server.Get(textureId, FileStorage.Type.png, sign.net.ID);
if (imageByte != null)
{
signData.Add($"texture{num}", Convert.ToBase64String(imageByte));
}
}
signData["amount"] = sign.textureIDs.Length;
}
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 cctvRc = entity as CCTV_RC;
if (cctvRc != null)
{
data.Add("cctv", new Dictionary<string, object>
{
{"yaw", cctvRc.yawAmount},
{"pitch", cctvRc.pitchAmount},
{"rcIdentifier", cctvRc.rcIdentifier}
});
}
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>();
var 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);
var outputs = new List<object>();
foreach (var output in ioEntity.outputs)
{
var ioConnection = new Dictionary<string, object>
{
{"connectedID", output.connectedTo.entityRef.uid},
{"connectedToSlot", output.connectedToSlot},
{"niceName", output.niceName},
{"type", (int) output.type},
{"linePoints", output.linePoints?.ToList() ?? new List<Vector3>()}
};
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 as 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)
{
var 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, int index)
{
if (!_signSizes.ContainsKey(sign.ShortPrefabName))
return;
var size = Math.Max(sign.paintableSources.Length, 1);
if (sign.textureIDs == null || sign.textureIDs.Length != size)
{
Array.Resize(ref sign.textureIDs, size);
}
var resizedImage = ImageResize(imageBytes, _signSizes[sign.ShortPrefabName].Width,
_signSizes[sign.ShortPrefabName].Height);
sign.textureIDs[index] = 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);
var ms = new MemoryStream();
resizedImage.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
private string Lang(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float diffRot)
{
var transformedPos = currentPos - initialPos;
var newX = transformedPos.x * (float) Math.Cos(-diffRot) +
transformedPos.z * (float) Math.Sin(-diffRot);
var newZ = transformedPos.z * (float) Math.Cos(-diffRot) -
transformedPos.x * (float) Math.Sin(-diffRot);
transformedPos.x = newX;
transformedPos.z = newZ;
return transformedPos;
}
private void Paste(ICollection<Dictionary<string, object>> entities, Dictionary<string, object> protocol,
bool ownership, Vector3 startPos, BasePlayer player, bool stability, float rotationCorrection,
float heightAdj, bool auth, Action callback, string filename)
{
var ioEntities = new Dictionary<uint, Dictionary<string, object>>();
uint buildingId = 0;
//Settings
var 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,
Ownership = ownership,
Callback = callback,
Filename = filename
};
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"];
var skinid = ulong.Parse(data["skinid"].ToString());
var pos = (Vector3) data["position"];
var rot = (Quaternion) data["rotation"];
var 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);
if (entity == null)
continue;
entity.transform.position = pos;
entity.transform.rotation = rot;
if (player != null)
entity.SendMessage("SetDeployedBy", player, SendMessageOptions.DontRequireReceiver);
if (pasteData.Ownership)
entity.OwnerID = ownerId;
var 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;
}
var decayEntity = entity as DecayEntity;
if (decayEntity != null)
{
if (pasteData.BuildingId == 0)
pasteData.BuildingId = BuildingManager.server.NewBuildingID();
decayEntity.AttachToBuilding(pasteData.BuildingId);
}
var stabilityEntity = entity as StabilityEntity;
if (stabilityEntity != null)
{
if (!stabilityEntity.grounded)
{
stabilityEntity.grounded = true;
pasteData.StabilityEntities.Add(stabilityEntity);
}
}
entity.skinID = skinid;
entity.Spawn();
var baseCombat = entity as BaseCombatEntity;
if (baseCombat != null)
baseCombat.SetHealth(baseCombat.MaxHealth());
pasteData.PastedEntities.AddRange(TryPasteSlots(entity, data, pasteData));
var box = entity as StorageContainer;
if (box != null)
{
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"))
{
var 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>;
var contentsItemId = Convert.ToInt32(contents["id"]);
if (pasteData.IsItemReplace)
contentsItemId = GetItemId(contentsItemId);
i.contents.AddItem(ItemManager.FindItemDefinition(contentsItemId),
Convert.ToInt32(contents["amount"]));
}
}
}
}
var targetPos = -1;
if (item.ContainsKey("position"))
targetPos = Convert.ToInt32(item["position"]);
i.position = targetPos;
box.inventory.Insert(i);
}
}
}
var autoTurret = entity as AutoTurret;
if (autoTurret != null)
{
var 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();
}
var containerIo = entity as ContainerIOEntity;
if (containerIo != null)
{
containerIo.inventory.Clear();
var items = new List<object>();
if (data.ContainsKey("items"))
items = data["items"] as List<object>;
foreach (var itemDef in items)
{
var itemJson = itemDef as Dictionary<string, object>;
var itemid = Convert.ToInt32(itemJson["id"]);
var itemamount = Convert.ToInt32(itemJson["amount"]);
var itemskin = ulong.Parse(itemJson["skinid"].ToString());
var itemcondition = Convert.ToSingle(itemJson["condition"]);
if (pasteData.IsItemReplace)
itemid = GetItemId(itemid);
var item = ItemManager.CreateByItemID(itemid, itemamount, itemskin);
if (item != null)
{
item.condition = itemcondition;
if (itemJson.ContainsKey("text"))
item.text = itemJson["text"].ToString();
if (itemJson.ContainsKey("blueprintTarget"))
{
var blueprintTarget = Convert.ToInt32(itemJson["blueprintTarget"]);
if (pasteData.IsItemReplace)
blueprintTarget = GetItemId(blueprintTarget);
item.blueprintTarget = blueprintTarget;
}
if (itemJson.ContainsKey("magazine"))
{
var heldent = item.GetHeldEntity();
if (heldent != null)
{
var projectiles = heldent.GetComponent<BaseProjectile>();
if (projectiles != null
// ... [Code preview truncated due to length. Full source in attached file] ...