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 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;
using Oxide.Core.Libraries.Covalence;
using System.Text;
using System.Text.RegularExpressions;
using Oxide.Core.Plugins;
using Oxide.Core.Configuration;
namespace Oxide.Plugins
{
[Info("Copy Paste", "Reneb & MiRror & Misstake & misticos & Shady & MBR", "4.1.22")]
[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, HashSet<BaseEntity>> undoEntities = new Dictionary<string, HashSet<BaseEntity>>();
private readonly Item _emptyItem = new Item { info = new ItemDefinition() };
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 }
[PluginReference]
Plugin DiscordAPI2;
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);
}
AddCovalenceCommand("paste", "cmdChatPaste");
AddCovalenceCommand("paste2", "cmdChatPasteManual");
AddCovalenceCommand("pasteall", "cmdChatPasteAll");
AddCovalenceCommand("undoall", "cmdChatUndoAll");
//PrintWarning("Added covalence commands");
}
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());
if (!Physics.Raycast(player.eyes.HeadRay(), out var 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);
if (!Physics.Raycast(player.eyes.HeadRay(), out var hit, 1000f, rayPaste))
return Lang("NO_ENTITY_RAY", player.UserIDString);
return TryPaste(hit.point, filename, player.UserIDString, 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)
{
const float maxDiff = 0.01f;
var ret = false;
var ents = Facepunch.Pool.GetList<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;
}
ret = true;
}
Facepunch.Pool.FreeList(ref ents);
return ret;
}
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], userIDString, 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>())
{
if (!storageContainer.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)
{
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(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.textureIDs[0], FileStorage.Type.png, sign.net.ID);
data.Add("sign", new Dictionary<string, object>
{
{"locked", sign.IsLocked()}
});
if (sign.textureIDs[0] > 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)
{
entity = null;
point = Vector3.zero;
if (!Physics.Raycast(sourcePos, sourceDir, out var 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.textureIDs[0] = FileStorage.server.Store(resizedImage, FileStorage.Type.png, sign.net.ID);
}
private object GetGround(Vector3 pos)
{
pos += new Vector3(0, 100, 0);
if (Physics.Raycast(pos, Vector3.down, out var 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 bool HasAccess(IPlayer player, string permName)
{
return player.IsServer || player.IsAdmin || permission.UserHasPermission(player.Id, permName);
}
string RemoveTags(string phrase)
{
if (string.IsNullOrEmpty(phrase)) return phrase;
// Forbidden formatting tags
var forbiddenTags = new List<string>{
"</color>",
"</size>",
"<b>",
"</b>",
"<i>",
"</i>"
};
// Replace Color Tags
phrase = new Regex("(<color=.+?>)").Replace(phrase, string.Empty);
// Replace Size Tags
phrase = new Regex("(<size=.+?>)").Replace(phrase, string.Empty);
var phraseSB = new StringBuilder(phrase);
for (int i = 0; i < forbiddenTags.Count; i++)
{
var tag = forbiddenTags[i];
phraseSB.Replace(tag, string.Empty);
}
return phraseSB.ToString();
}
void SendReply(IPlayer player, string msg, bool keepTagsConsole = false)
{
if (player == null) return;
msg = !player.IsServer ? msg : (keepTagsConsole) ? msg : RemoveTags(msg);
if (player.IsServer) ConsoleSystem.CurrentArgs.ReplyWith(msg);
else player.Reply(msg);
}
private Vector3 GetVector3FromString(string vectorStr)
{
var vector = Vector3.zero;
if (string.IsNullOrEmpty(vectorStr)) return vector;
var vecStr = vectorStr;
if (vecStr.StartsWith("(") && vecStr.EndsWith(")")) vecStr = vecStr.Substring(1, vecStr.Length - 2);
var split = vecStr.Split(',');
return (split == null || split.Length < 3) ? vector : new Vector3(Convert.ToSingle(split[0]), Convert.ToSingle(split[1]), Convert.ToSingle(split[2]));
}
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, string userId, 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,
UserID = userId,
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 userId = pasteData.UserID;
if (!string.IsNullOrWhiteSpace(pasteData?.UserID) || !ulong.TryParse(userId, out var userIdUL))
userIdUL = 0;
//has cache (relationshipmanager)
var player = RelationshipManager.FindByID(userIdUL); //BasePlayer.FindByID(userIdUL) ?? BasePlayer.FindSleeping(userIdUL);
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 = userIdUL;
if (data.TryGetValue("ownerid", out var dataOwnerId))
ownerId = Convert.ToUInt64(dataOwnerId);
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)
{
var nearBy = decayEntity.GetNearbyBuildingBlock();
if (nearBy != null && nearBy.buildingID != 0)
decayEntity.AttachToBuilding(nearBy.buildingID);
else decayEntity.AttachToBuilding(BuildingManager.server.NewBuildingID());
}
else 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.health = baseCombat.MaxHealth();
//baseCombat.ChangeHealth(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.TryGetValue("items", out var dataItems))
items = dataItems 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.TryGetValue("text", out var dataText))
i.text = dataText.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);
}
}
}
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 (userIdUL > 0 && !authorizedPlayers.Contains(userIdUL) && pasteData.Auth)
authorizedPlayers.Add(userIdUL);
foreach (var userID in authorizedPlayers)
{
autoturret.authorizedPlayers.Add(new PlayerNameID()
{
userid = userID,
username = (BasePlayer.FindByID(userID) ?? BasePlayer.FindSleeping(userID))?.displayName ?? "Unknown Name"
});
}
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 (userIdUL > 0 && !authorizedPlayers.Contains(userIdUL) && pasteData.Auth)
authorizedPlayers.Add(userIdUL);
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);
// ... [Code preview truncated due to length. Full source in attached file] ...