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 Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Libraries.Covalence;
using ProtoBuf;
using UnityEngine;
using Graphics = System.Drawing.Graphics;
// ReSharper disable SpecifyACultureInStringConversionExplicitly
/*
* CREDITS
*
* Orange - Saving ContainerIOEntity
* UIP88 - Turrets fix
* bsdinis - Wire fix
* nivex - Ownership option, sign fix
* DezLife - CCTV fix
* Wulf - Skipping 4.1.24 :D
*
*/
namespace Oxide.Plugins
{
[Info("Copy Paste", "misticos", "4.1.36")]
[Description("Copy and paste buildings to save them or move them")]
public class CopyPaste : CovalencePlugin
{
// ReSharper disable once Unity.IncorrectMonoBehaviourInstantiation
private readonly Item _emptyItem = new Item { info = new ItemDefinition() };
private readonly IPlayer _consolePlayer = new RustConsolePlayer();
private int _copyLayer =
LayerMask.GetMask("Construction", "Prevent Building", "Construction Trigger", "Trigger", "Deployed",
"Default", "Ragdoll"),
_groundLayer = LayerMask.GetMask("Terrain", "Default"),
_rayCopy = LayerMask.GetMask("Construction", "Deployed", "Tree", "Resource", "Prevent Building"),
_rayPaste = LayerMask.GetMask("Construction", "Deployed", "Tree", "Terrain", "World", "Water",
"Prevent Building");
private string _copyPermission = "copypaste.copy",
_listPermission = "copypaste.list",
_pastePermission = "copypaste.paste",
_pastebackPermission = "copypaste.pasteback",
_undoPermission = "copypaste.undo",
_subDirectory = "copypaste/";
private Dictionary<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 = players.FindPlayerById(userId.ToString())?.Object as BasePlayer;
if (player == null)
return Lang("NOT_FOUND_PLAYER");
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayCopy))
return Lang("NO_ENTITY_RAY", player.UserIDString);
return TryCopy(hit.point, hit.GetEntity().GetNetworkRotation().eulerAngles, filename,
DegreeToRadian(player.GetNetworkRotation().eulerAngles.y), args, player.IPlayer, callback);
}
private object TryPasteFromSteamId(ulong userId, string filename, string[] args, Action callback = null,
Action<BaseEntity> callbackSpawned = null)
{
var player = players.FindPlayerById(userId.ToString())?.Object as BasePlayer;
if (player == null)
return Lang("NOT_FOUND_PLAYER");
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 1000f, _rayPaste))
return Lang("NO_ENTITY_RAY", player.UserIDString);
return TryPaste(hit.point, filename, player.IPlayer,
DegreeToRadian(player.GetNetworkRotation().eulerAngles.y),
args, callback: callback, callbackSpawned: callbackSpawned).Item1;
}
private object TryPasteFromVector3(Vector3 pos, float rotationCorrection, string filename, string[] args,
Action callback = null, Action<BaseEntity> callbackSpawned = null)
{
return TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback: callback,
callbackSpawned: callbackSpawned).Item1;
}
private ValueTuple<object, Action> TryPasteFromVector3Cancellable(Vector3 pos, float rotationCorrection,
string filename, string[] args,
Action callback = null, Action<BaseEntity> callbackSpawned = null)
{
var result = TryPaste(pos, filename, _consolePlayer, rotationCorrection, args, callback: callback,
callbackSpawned: callbackSpawned);
var pasteData = result.Item2;
return new ValueTuple<object, Action>(result.Item1, () => pasteData.Cancelled = true);
}
#endregion
//Other methods
private object CheckCollision(HashSet<Dictionary<string, object>> entities, Vector3 startPos, float radius)
{
foreach (var entityobj in entities)
{
if (Physics.CheckSphere((Vector3)entityobj["position"], radius, _copyLayer))
return Lang("BLOCKING_PASTE");
}
return true;
}
private bool CheckPlaced(string prefabname, Vector3 pos, Quaternion rot)
{
const float maxDiff = 0.01f;
var ents = Pool.GetList<BaseEntity>();
try
{
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;
}
finally
{
Pool.FreeList(ref ents);
}
}
private void UndoLoop(HashSet<BaseEntity> entities, IPlayer 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();
}
var io = p as IOEntity;
if (io != null)
{
io.ClearConnections();
}
if (p != null && !p.IsDestroyed)
p.Kill();
});
// If it gets stuck in infinite loop break the loop.if (count != 0 && entities.Count != 0 && entities.Count == count)
{
player?.Reply("Undo cancelled because of infinite loop.");
return;
}
if (entities.Count > 0)
NextTick(() => UndoLoop(entities, player, entities.Count));
else if (player != null)
{
player.Reply(Lang("UNDO_SUCCESS", player.Id));
if (_lastPastes.ContainsKey(player.Id) && _lastPastes[player.Id].Count == 0)
_lastPastes.Remove(player.Id);
}
}
private void Copy(Vector3 sourcePos, Vector3 sourceRot, string filename, float rotationCorrection,
CopyMechanics copyMechanics, float range, bool saveTree, bool saveShare, bool eachToEach, IPlayer player,
Action callback)
{
var currentLayer = _copyLayer;
if (saveTree)
currentLayer |= LayerMask.GetMask("Tree");
var copyData = new CopyData
{
Filename = filename,
CurrentLayer = currentLayer,
RotCor = rotationCorrection,
Range = range,
SaveShare = saveShare,
SaveTree = saveTree,
CopyMechanics = copyMechanics,
EachToEach = eachToEach,
SourcePos = sourcePos,
SourceRot = sourceRot,
Player = player,
BasePlayer = player.Object as BasePlayer,
Callback = callback
};
copyData.CheckFrom.Push(sourcePos);
NextTick(() => CopyLoop(copyData));
}
// Main loop for copy, will fetch all the data needed.Is called every tick untill copy is done (can't find any entities)
private void CopyLoop(CopyData copyData)
{
var checkFrom = copyData.CheckFrom;
var houseList = copyData.HouseList;
var buildingId = copyData.BuildingId;
var copyMechanics = copyData.CopyMechanics;
var batchSize = checkFrom.Count < _config.CopyBatchSize ? checkFrom.Count : _config.CopyBatchSize;
for (var i = 0; i < batchSize; i++)
{
if (checkFrom.Count == 0)
break;
var list = Pool.GetList<BaseEntity>();
try
{
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;
}
}
var transform = entity.transform;
if (copyData.EachToEach)
checkFrom.Push(transform.position);
if (entity.GetComponent<BaseLock>() != null)
continue;
copyData.RawData.Add(EntityData(entity, transform.position,
transform.rotation.eulerAngles / 57.29578f, copyData));
}
}
finally
{
Pool.FreeList(ref list);
}
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);
copyData.Player.Reply(Lang("COPY_SUCCESS", copyData.Player.Id, copyData.Filename));
copyData.Callback?.Invoke();
Interface.CallHook("OnCopyFinished", copyData.RawData, copyData.Filename, copyData.Player, copyData.SourcePos);
}
}
private float DegreeToRadian(float angle)
{
return (float)(Math.PI * angle / 180.0f);
}
private Dictionary<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 }
};
var growableEntity = entity as GrowableEntity;
if (growableEntity != null)
{
var genes = GrowableGeneEncoding.EncodeGenesToInt(growableEntity.Genes);
if (genes > 0)
{
data.Add("genes", genes);
}
var perent = growableEntity.GetParentEntity();
if (perent != null)
{
data.Add("hasParent", true);
}
}
TryCopySlots(entity, data, copyData.SaveShare);
var 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 },
{ "dataInt", item.instanceData?.dataInt ?? 0 }
};
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 },
{ "dataInt", item.instanceData?.dataInt ?? 0 }
};
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 (var 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;
}
var lights = entity as AdvancedChristmasLights;
if (lights != null)
{
data.Add("points", lights.points.Select(x => new { x.normal, x.point }));
data.Add("animationStyle", lights.animationStyle);
}
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.Value },
{ "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.Value },
{ "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.Value);
var electricalBranch = ioEntity as ElectricalBranch;
if (electricalBranch != null)
{
ioData.Add("branchAmount", electricalBranch.branchAmount);
}
var counter = ioEntity as PowerCounter;
if (counter != null)
{
ioData.Add("targetNumber", counter.GetTarget());
}
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)
{
var prefab = (string)entity["prefabname"];
if (prefab.Contains("/foundation/") || prefab.Contains("/foundation.triangle/"))
{
var foundHeight = GetGround((Vector3)entity["position"]);
if (foundHeight != null)
{
var height = (Vector3)foundHeight;
if (height.y > maxHeight)
maxHeight = height.y;
}
}
}
maxHeight += 1f;
return maxHeight;
}
private bool FindRayEntity(Vector3 sourcePos, Vector3 sourceDir, out Vector3 point, out BaseEntity entity,
int rayLayer)
{
RaycastHit hitinfo;
entity = null;
point = Vector3.zero;
if (!Physics.Raycast(sourcePos, sourceDir, out hitinfo, 1000f, rayLayer))
return false;
entity = hitinfo.GetEntity();
point = hitinfo.point;
return true;
}
private 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(IPlayer player, string permName)
{
return player.IsAdmin || player.HasPermission(permName);
}
private byte[] ImageResize(byte[] imageBytes, int width, int height)
{
Bitmap resizedImage = new Bitmap(width, height),
sourceImage = new Bitmap(new MemoryStream(imageBytes));
Graphics.FromImage(resizedImage).DrawImage(sourceImage, new Rectangle(0, 0, width, height),
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel);
var ms = new MemoryStream();
resizedImage.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
private string Lang(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private Vector3 NormalizePosition(Vector3 initialPos, Vector3 currentPos, float diffRot)
{
var transformedPos = currentPos - initialPos;
var newX = transformedPos.x * (float)Math.Cos(-diffRot) +
transformedPos.z * (float)Math.Sin(-diffRot);
var newZ = transformedPos.z * (float)Math.Cos(-diffRot) -
transformedPos.x * (float)Math.Sin(-diffRot);
transformedPos.x = newX;
transformedPos.z = newZ;
return transformedPos;
}
private PasteData Paste(ICollection<Dictionary<string, object>> entities, Dictionary<string, object> protocol,
bool ownership, Vector3 startPos, IPlayer player, bool stability, float rotationCorrection,
float heightAdj, bool auth, Action callback, Action<BaseEntity> callbackSpawned, string filename, bool checkPlaced)
{
//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,
BasePlayer = player.Object as BasePlayer,
QuaternionRotation = quaternionRotation,
StartPos = startPos,
Stability = stability,
Auth = auth,
Ownership = ownership,
CallbackFinished = callback,
CallbackSpawned = callbackSpawned,
Filename = filename,
CheckPlaced = checkPlaced
};
NextTick(() => PasteLoop(pasteData));
return pasteData;
}
private void PasteLoop(PasteData pasteData)
{
if (pasteData.Cancelled)
{
UndoLoop(new HashSet<BaseEntity>(pasteData.PastedEntities), pasteData.Player,
pasteData.PastedEntities.Count);
return;
}
var entities = pasteData.Entities;
var todo = entities.Take(_config.PasteBatchSize).ToArray();
foreach (var data in todo)
{
entities.Remove(data);
var prefabname = (string)data["prefabname"];
#if DEBUG
Puts($"{nameof(PasteLoop)}: Entity {prefabname}");
#endif
var skinid = ulong.Parse(data["skinid"].ToString());
var pos = (Vector3)data["position"];
var rot = (Quaternion)data["rotation"];
var ownerId = pasteData.BasePlayer?.userID ?? 0;
if (data.ContainsKey("ownerid"))
{
#if DEBUG
Puts($"{nameof(PasteLoop)}: Convert.ToUInt64 1077");
#endif
ownerId = Convert.ToUInt64(data["ownerid"]);
}
if (pasteData.CheckPlaced && CheckPlaced(prefabname, pos, rot))
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;
var transform = entity.transform;
transform.position = pos;
transform.rotation = rot;
if (pasteData.BasePlayer != null)
entity.SendMessage("SetDeployedBy", pasteData.BasePlayer, SendMessageOptions.DontRequireReceiver);
if (pasteData.Ownership)
entity.OwnerID = ownerId;
var buildingBlock = entity as BuildingBlock;
if (buildingBlock != null)
{
buildingBlock.blockDefinition = PrefabAttribute.server.Find<Construction>(buildingBlock.prefabID);
if (buildingBlock.skinID != 10220) buildingBlock.skinID = 0uL;
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);
}
}
if (buildingBlock == null)
entity.skinID = skinid;
entity.Spawn();
var baseCombat = entity as BaseCombatEntity;
if (buildingBlock != null)
{
buildingBlock.SetHealthToMax();
buildingBlock.SendNetworkUpdate();
buildingBlock.UpdateSkin();
}
else if (baseCombat != null)
baseCombat.SetHealth(baseCombat.MaxHealth());
pasteData.PastedEntities.AddRange(TryPasteSlots(entity, data, pasteData));
var box = entity as StorageContainer;
if (box != null)
{
if (box.inventory == null)
{
box.inventory = new ItemContainer();
box.inventory.ServerInitialize(null, box.inventorySlots);
box.inventory.GiveUID();
box.inventory.entityOwner = box;
}
else 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"]);
var dataInt = 0;
if (item.ContainsKey("dataInt"))
{
dataInt = Convert.ToInt32(item["dataInt"]);
}
var growableEntity = entity as GrowableEntity;
if (growableEntity != null)
{
if (data.ContainsKey("genes"))
{
var genesData = (int)data["genes"];
if (genesData > 0)
{
GrowableGeneEncoding.DecodeIntToGenes(genesData, growableEntity.Genes);
}
}
if (data.ContainsKey("hasParent"))
{
var isParented = (bool)data["hasParent"];
if (isParented)
{
RaycastHit hitInfo;
if (Physics.Raycast(growableEntity.transform.position, Vector3.down, out hitInfo,.5f, Rust.Layers.DefaultDeployVolumeCheck))
{
var parentEntity = hitInfo.GetEntity();
if (parentEntity != null)
{
growableEntity.SetParent(parentEntity, true);
}
}
}
}
}
if (pasteData.IsItemReplace)
itemid = GetItemId(itemid);
var i = ItemManager.CreateByItemID(itemid, itemamount, itemskin);
if (i != null)
{
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 (dataInt > 0)
{
i.instanceData = new ProtoBuf.Item.InstanceData()
{
ShouldPool = false,
dataInt = dataInt
};
}
if (item.ContainsKey("magazine"))
{
var heldent = i.GetHeldEntity();
if (heldent != null)
{
var projectiles = heldent.GetComponent<BaseProjectile>();
if (projectiles != null)
{
// ... [Code preview truncated due to length. Full source in attached file] ...