Guest User

EntityOwner.cs

a guest
Mar 7th, 2026
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 73.06 KB | Gaming | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Facepunch;
  6. using Oxide.Core.Libraries.Covalence;
  7. using Oxide.Core.Plugins; // needed for Oxide compat
  8. using UnityEngine;
  9.  
  10. namespace Oxide.Plugins;
  11.  
  12. [Info("Entity Owner", "Calytic", "3.5.0")]
  13. [Description("Modify entity ownership and cupboard/turret authorization")]
  14. class EntityOwner : RustPlugin
  15. {
  16.     #region Data & Config
  17.  
  18.     private readonly int _layerMasks = LayerMask.GetMask("Construction", "Construction Trigger", "Trigger", "Deployed");
  19.  
  20.     private int _entityLimit = 8000;
  21.     private float _distanceThreshold = 3f;
  22.     private float _cupboardDistanceThreshold = 20f;
  23.     private float _wheelDistanceThreshold = 20f;
  24.  
  25.     private bool _debug;
  26.  
  27.     private static readonly object FalseObject = false;
  28.     private static readonly string[] DefaultArg = { "1" };
  29.  
  30.     // this number was chosen based on testing, and looking at Facepunch code
  31.     // a warning will be logged in the case that it may be too small
  32.     private const int RaycastHitsMax = 64;
  33.     private readonly RaycastHit[] _raycastHits = new RaycastHit[RaycastHitsMax];
  34.  
  35.     #endregion
  36.  
  37.     #region Data Handling & Initialization
  38.  
  39.     private readonly Dictionary<string, string> _texts = new()
  40.     {
  41.         {"Denied: Permission", "You are not allowed to use this command"},
  42.         {"Error: Unknown", "Undefined error occurred. Look up the log for more info"},
  43.         {"Target: None", "No target found"},
  44.         {"Target: Owner", "Owner: {0}"},
  45.         {"Target: Limit", "Exceeded entity limit"},
  46.         {"Syntax: Owner", "Invalid syntax: /owner"},
  47.         {"Syntax: SetOwner", "Incorrect Syntax.\n/setowner <playername|steamid>"},
  48.         {"Syntax: Own", "Invalid Syntax. \n/own type player\nTypes: all/block/storage/cupboard/sign/sleepingbag/plant/oven/door/turret/wheel\n/own player"},
  49.         {"Syntax: Unown", "Invalid Syntax. \n/unown type player\nTypes: all/block/storage/cupboard/sign/sleepingbag/plant/oven/door/turret/wheel\n/unown player"},
  50.         {"Syntax: Prod2", "Invalid Syntax. \n/prod2 type \nTypes:\n all/block/entity/storage/cupboard/sign/sleepingbag/plant/oven/door/turret/wheel"},
  51.         {"Syntax: Auth", "Invalid Syntax. \n/auth turret player\n/auth cupboard player\n/auth wheel player\n/auth player\n/auth"},
  52.         {"Syntax: Deauth", "Invalid Syntax. \n/deauth turret player\n/deauth cupboard player\n/deauth wheel player\n/deauth player\n/deauth"},
  53.         {"Ownership: Changing", "Changing ownership.."},
  54.         {"Ownership: Removing", "Removing ownership.."},
  55.         {"Ownership: New", "New owner of all around is: {0}"},
  56.         {"Ownership: New Self", "Owner: You were given ownership of this house and nearby deployables"},
  57.         {"Ownership: Count", "Count ({0})"},
  58.         {"Ownership: Removed", "Ownership removed"},
  59.         {"Ownership: Changed", "Ownership changed"},
  60.         {"Entities: None", "No entities found"},
  61.         {"Entities: Authorized", "({0}) Authorized"},
  62.         {"Entities: Count", "Counted {0} entities ({1}/{2})"},
  63.         {"Structure: Prodding","Prodding structure.."},
  64.         {"Structure: Condition Percent", "Condition: {0}%"},
  65.         {"Player: Unknown Percent", "Unknown: {0}%"},
  66.         {"Player: None", "Target player not found"},
  67.         {"Cupboards: Prodding", "Prodding cupboards.."},
  68.         {"Cupboards: Authorizing", "Authorizing cupboards.."},
  69.         {"Cupboards: Authorized", "Authorized {0} on {1} cupboards"},
  70.         {"Cupboards: Deauthorizing", "Deauthorizing cupboards.."},
  71.         {"Cupboards: Deauthorized", "Deauthorized {0} on {1} cupboards"},
  72.         {"Turrets: Authorized", "Authorized {0} on {1} turrets"},
  73.         {"Turrets: Authorizing", "Authorizing turrets.."},
  74.         {"Turrets: Prodding", "Prodding turrets.."},
  75.         {"Turrets: Deauthorized", "Deauthorized {0} on {1} turrets"},
  76.         {"Turrets: Deauthorizing", "Deauthorizing turrets.."},
  77.         {"Wheels: Prodding", "Prodding wheels.."},
  78.         {"Wheels: Authorizing", "Authorizing wheels.."},
  79.         {"Wheels: Authorized", "Authorized {0} on {1} wheels"},
  80.         {"Wheels: Deauthorizing", "Deauthorizing wheels.."},
  81.         {"Wheels: Deauthorized", "Deauthorized {0} on {1} wheels"},
  82.         {"Lock: Code", "Code: {0}"},
  83.         {"Lock: Owner", "Lock owner: {0}"},
  84.         {"Bag: Assignee", "Assigned: {0}"}
  85.     };
  86.  
  87.     // Loads the default configuration
  88.     protected override void LoadDefaultConfig()
  89.     {
  90.         Config["Debug"] = false;
  91.         Config["EntityLimit"] = _entityLimit;
  92.         Config["DistanceThreshold"] = _distanceThreshold;
  93.         Config["CupboardDistanceThreshold"] = _cupboardDistanceThreshold;
  94.         Config["WheelDistanceThreshold"] = _wheelDistanceThreshold;
  95.         Config["VERSION"] = Version.ToString();
  96.         Config.Save();
  97.     }
  98.  
  99.     private new void LoadDefaultMessages()
  100.     {
  101.         lang.RegisterMessages(_texts, this);
  102.     }
  103.  
  104.     protected void ReloadConfig()
  105.     {
  106.         Config["VERSION"] = Version.ToString();
  107.  
  108.         // NEW CONFIGURATION OPTIONS HERE
  109.         Config["Debug"] = false;
  110.         Config["EntityLimit"] = _entityLimit;
  111.         Config["DistanceThreshold"] = _distanceThreshold;
  112.         Config["CupboardDistanceThreshold"] = _cupboardDistanceThreshold;
  113.         Config["WheelDistanceThreshold"] = _wheelDistanceThreshold;
  114.         // END NEW CONFIGURATION OPTIONS
  115.  
  116.         PrintToConsole("Upgrading Configuration File");
  117.         SaveConfig();
  118.     }
  119.  
  120.     // Gets a config value of a specific type
  121.     private T GetConfig<T>(string name, T defaultValue) =>
  122.         Config[name] == null ? defaultValue : (T)Convert.ChangeType(Config[name], typeof(T));
  123.  
  124.     private string GetMsg(string key, BasePlayer player = null) =>
  125.         lang.GetMessage(key, this, player?.UserIDString);
  126.  
  127.     private void OnServerInitialized()
  128.     {
  129.         try
  130.         {
  131.             LoadConfig();
  132.  
  133.             _debug = GetConfig("Debug", false);
  134.             _entityLimit = GetConfig("EntityLimit", 8000);
  135.             _distanceThreshold = GetConfig("DistanceThreshold", 3f);
  136.             _cupboardDistanceThreshold = GetConfig("CupboardDistanceThreshold", 20f);
  137.             _wheelDistanceThreshold = GetConfig("WheelDistanceThreshold", 20f);
  138.  
  139.             if (_distanceThreshold >= 5)
  140.             {
  141.                 PrintWarning("ALERT: Distance threshold configuration option is ABOVE 5.  This may cause serious performance degradation (lag) when using EntityOwner commands");
  142.             }
  143.  
  144.             permission.RegisterPermission("entityowner.cancheckowners", this);
  145.             permission.RegisterPermission("entityowner.cancheckcodes", this);
  146.             permission.RegisterPermission("entityowner.canchangeowners", this);
  147.             permission.RegisterPermission("entityowner.seedetails", this);
  148.  
  149.             LoadData();
  150.         }
  151.         catch (Exception ex)
  152.         {
  153.             PrintError("OnServerInitialized failed: {0}", ex.Message);
  154.         }
  155.     }
  156.  
  157.     private void LoadData()
  158.     {
  159.         if (
  160.             // FOR COMPATIBILITY WITH INITIAL VERSIONS WITHOUT VERSIONED CONFIG
  161.             Config["VERSION"] == null ||
  162.             // ADDS NEW, IF ANY, CONFIGURATION OPTIONS
  163.             GetConfig("VERSION", Version.ToString()) !=
  164.                 Version.ToString())
  165.         {
  166.             ReloadConfig();
  167.         }
  168.     }
  169.  
  170.     [HookMethod("SendHelpText")]
  171.     private void SendHelpText(BasePlayer player)
  172.     {
  173.         var sb = Pool.Get<StringBuilder>();
  174.         if (canCheckOwners(player) || canChangeOwners(player))
  175.         {
  176.             sb.Append("<size=18>EntityOwner</size> by <color=#ce422b>Calytic</color> at <color=#ce422b>http://rustservers.io</color>\n");
  177.         }
  178.  
  179.         if (canCheckOwners(player))
  180.         {
  181.             sb.Append("  ").Append("<color=\"#ffd479\">/prod</color> - Check ownership of entity you are looking at").Append("\n");
  182.             sb.Append("  ").Append("<color=\"#ffd479\">/prod2</color> - Check ownership of entire structure/all deployables").Append("\n");
  183.             sb.Append("  ").Append("<color=\"#ffd479\">/prod2 block</color> - Check ownership structure only").Append("\n");
  184.             sb.Append("  ").Append("<color=\"#ffd479\">/prod2 cupboard</color> - Check authorization on all nearby cupboards").Append("\n");
  185.             sb.Append("  ").Append("<color=\"#ffd479\">/auth</color> - Check authorization list of tool cupboard you are looking at").Append("\n");
  186.         }
  187.  
  188.         if (canChangeOwners(player))
  189.         {
  190.             sb.Append("  ").Append("<color=\"#ffd479\">/own [all/block]</color> - Take ownership of entire structure").Append("\n");
  191.             sb.Append("  ").Append("<color=\"#ffd479\">/own [all/block] PlayerName</color> - Give ownership of entire structure to specified player").Append("\n");
  192.             sb.Append("  ").Append("<color=\"#ffd479\">/unown [all/block]</color> - Remove ownership from entire structure").Append("\n");
  193.             sb.Append("  ").Append("<color=\"#ffd479\">/auth PlayerName</color> - Authorize specified player on all nearby cupboards").Append("\n");
  194.         }
  195.  
  196.         SendReply(player, sb.ToString());
  197.         Pool.FreeUnmanaged(ref sb);
  198.     }
  199.  
  200.     #endregion
  201.  
  202.     #region Chat Commands
  203.  
  204.     [ChatCommand("prod")]
  205.     private void cmdProd(BasePlayer player, string command, string[] args)
  206.     {
  207.         if (!canCheckOwners(player))
  208.         {
  209.             SendReply(player, GetMsg("Denied: Permission", player));
  210.             return;
  211.         }
  212.  
  213.         if (args?.Length != 0)
  214.         {
  215.             SendReply(player, GetMsg("Syntax: Owner", player));
  216.             return;
  217.         }
  218.  
  219.         //var input = serverinput.GetValue(player) as InputState;
  220.         //var currentRot = Quaternion.Euler(input.current.aimAngles) * Vector3.forward;
  221.         //var target = RaycastAll<BaseEntity>(player.transform.position + new Vector3(0f, 1.5f, 0f), currentRot);
  222.         var target = RaycastAll<BaseEntity>(player.eyes.HeadRay());
  223.         if (target is bool)
  224.         {
  225.             SendReply(player, GetMsg("Target: None", player));
  226.             return;
  227.         }
  228.  
  229.         if (target is not BaseEntity targetEntity) return;
  230.         var owner = GetOwnerName(targetEntity);
  231.         if (string.IsNullOrEmpty(owner))
  232.         {
  233.             owner = "N/A";
  234.         }
  235.  
  236.         var sb = Pool.Get<StringBuilder>();
  237.         sb.AppendFormat(GetMsg("Target: Owner", player), owner);
  238.  
  239.         var baseLock = targetEntity.GetSlot(BaseEntity.Slot.Lock) as BaseLock;
  240.         if ((baseLock?.OwnerID ?? 0) != 0 && baseLock.OwnerID != targetEntity.OwnerID)
  241.         {
  242.             var lockOwnerName = GetOwnerName(baseLock);
  243.             sb.AppendLine().AppendFormat(GetMsg("Lock: Owner", player), lockOwnerName);
  244.         }
  245.  
  246.         if (canSeeDetails(player))
  247.         {
  248.             sb.AppendLine().Append("<color=#D3D3D3>ID (net/prefab/instance): ").Append(targetEntity.net.ID).Append('/')
  249.                 .Append(targetEntity.prefabID).Append('/').Append(targetEntity.GetInstanceID()).Append("</color>");
  250.             sb.AppendLine().Append("<color=#D3D3D3>Name: ").Append(targetEntity.ShortPrefabName).Append("</color>");
  251.             sb.AppendLine().Append("<color=#D3D3D3>Type: ").Append(targetEntity.GetType()).Append("</color>");
  252.             if (targetEntity.skinID > 0)
  253.             {
  254.                 sb.AppendLine().Append("<color=#D3D3D3>Skin: ").Append(targetEntity.skinID).Append("</color>");
  255.             }
  256.  
  257.             if (targetEntity.PrefabName != targetEntity.ShortPrefabName)
  258.             {
  259.                 sb.AppendLine().Append("<color=#D3D3D3>Prefab: \"").Append(targetEntity.PrefabName).Append("\"</color>");
  260.             }
  261.  
  262.             sb.AppendLine().Append("<color=#D3D3D3>Outside: ").Append(targetEntity.IsOutside() ? "Yes" : "No").Append("</color>");
  263.  
  264.             if (targetEntity is BaseCombatEntity bcEntity)
  265.             {
  266.                 sb.AppendLine().Append("<color=#D3D3D3>Health: ").Append(bcEntity.health).Append('/')
  267.                     .Append(bcEntity.MaxHealth()).Append(" (").Append(bcEntity.healthFraction * 100.0f)
  268.                     .Append("%)</color>");
  269.             }
  270.  
  271.             if (targetEntity is BuildingBlock buildingBlock)
  272.             {
  273.                 sb.AppendLine().Append("<color=#D3D3D3>Grade: ").Append(buildingBlock.grade).Append("</color>");
  274.             }
  275.         }
  276.  
  277.         if (canCheckCodes(player))
  278.         {
  279.             var code = baseLock is CodeLock codeLock ? codeLock.code : targetEntity switch
  280.             {
  281.                 // lock associated with modular car module's chassis
  282.                 BaseVehicleModule { Vehicle: ModularCar modularCar } => modularCar.CarLock?.Code,
  283.                 // lock associated with modular car chassis
  284.                 ModularCar car => car.CarLock?.Code,
  285.                 // completed boat wheel lock
  286.                 PlayerBoat boat => boat.GetSteeringWheel()?.BoatLock?.Code,
  287.                 // camera RF ID
  288.                 PoweredRemoteControlEntity camera => camera.rcIdentifier,
  289.                 // drone RF ID
  290.                 RemoteControlEntity drone => drone.rcIdentifier,
  291.                 // boat in edit mode
  292.                 SteeringWheel wheel => wheel.BoatLock?.Code,
  293.                 _ => null
  294.             };
  295.             if (!string.IsNullOrEmpty(code))
  296.             {
  297.                 sb.AppendLine().AppendFormat(GetMsg("Lock: Code", player), code);
  298.             }
  299.         }
  300.  
  301.         if (targetEntity is SleepingBag bag && canCheckAssignee(player))
  302.         {
  303.             sb.AppendLine().AppendFormat(GetMsg("Bag: Assignee", player), FindPlayerName(bag.deployerUserID));
  304.         }
  305.  
  306.         SendReply(player, sb.ToString());
  307.         Pool.FreeUnmanaged(ref sb);
  308.     }
  309.  
  310.     //or any wanted new chat command. maybe even integrated in existing /own
  311.     //this one will only change ownership of the one particular entity you are looking at
  312.     [ChatCommand("setowner")]
  313.     private void cmdSetowner(BasePlayer player, string command, string[] args)
  314.     {
  315.         //check if user has permission
  316.         if (!canChangeOwners(player))
  317.         {
  318.             SendReply(player, GetMsg("Denied: Permission", player));
  319.             return;
  320.         }
  321.  
  322.         //check if given argument exists and is not empty
  323.         if (string.IsNullOrEmpty(args[0]))
  324.         {
  325.             SendReply(player, GetMsg("Syntax: SetOwner", player));
  326.             return;
  327.         }
  328.  
  329.         //getting valid UserID
  330.         var newOwnerid = FindUserIDByPartialName(args[0]);
  331.         if (newOwnerid == 0)
  332.         {
  333.             SendReply(player, GetMsg("Player: None", player));
  334.             return;
  335.         }
  336.  
  337.         try
  338.         {
  339.             //try to get the entity user is looking at
  340.             var targetEntity = RaycastAll<BaseEntity>(player.eyes.HeadRay());
  341.  
  342.             if (targetEntity is bool)
  343.             {
  344.                 SendReply(player, GetMsg("Target: None", player));
  345.                 return;
  346.             }
  347.  
  348.             if (targetEntity is not BaseEntity targetBaseEntity) return;
  349.             //just to make future usage of the var easier
  350.             try
  351.             {
  352.                 //get the current owner
  353.                 var oldOwner = GetOwnerName(targetBaseEntity);
  354.                 if (string.IsNullOrEmpty(oldOwner))
  355.                 {
  356.                     oldOwner = "N/A";
  357.                 }
  358.  
  359.                 ChangeOwner(targetBaseEntity, newOwnerid);
  360.  
  361.                 //check if the owner was actually changed
  362.                 var newOwner = GetOwnerName(targetBaseEntity);
  363.                 if (string.IsNullOrEmpty(newOwner))
  364.                 {
  365.                     newOwner = "N/A";
  366.                 }
  367.  
  368.                 SendReply(player, "Changed owner of [" + targetBaseEntity.ShortPrefabName + "] from [" + oldOwner + "] to [" + newOwner + "]");
  369.  
  370.             }
  371.             catch (Exception ex)
  372.             {
  373.                 SendReply(player, GetMsg("Error: Unknown", player));
  374.                 Debug.LogException(ex);
  375.             }
  376.         }
  377.         catch (Exception ex)
  378.         {
  379.             SendReply(player, GetMsg("Error: Unknown", player));
  380.             Debug.LogException(ex);
  381.         }
  382.     }
  383.  
  384.     [ChatCommand("own")]
  385.     private void cmdOwn(BasePlayer player, string command, string[] args)
  386.     {
  387.         if (!canChangeOwners(player))
  388.         {
  389.             SendReply(player, GetMsg("Denied: Permission", player));
  390.             return;
  391.         }
  392.  
  393.         var massTrigger = false;
  394.         string type = null;
  395.         ulong target = player.userID;
  396.  
  397.         if (args.Length == 0)
  398.         {
  399.             args = DefaultArg;
  400.         }
  401.         switch (args.Length)
  402.         {
  403.             case > 2:
  404.                 SendReply(player, GetMsg("Syntax: Own", player));
  405.                 return;
  406.             // this does nothing because type is null
  407.             // case 1 when type == "all" || type == "storage" || type == "block" || type == "cupboard" || type == "sign" || type == "sleepingbag" || type == "plant" || type == "oven" || type == "door" || type == "turret":
  408.             // {
  409.             //     massTrigger = true;
  410.             //     target = player.userID;
  411.             //     break;
  412.             // }
  413.             // this does nothing because type is null
  414.             // case 1 when !string.IsNullOrEmpty(type):
  415.             // {
  416.             //     target = FindUserIDByPartialName(type);
  417.             //     type = "1";
  418.             //     if (target == 0)
  419.             //     {
  420.             //         SendReply(player, GetMsg("Player: None", player));
  421.             //     }
  422.             //     else
  423.             //     {
  424.             //         massTrigger = true;
  425.             //     }
  426.             //
  427.             //     break;
  428.             // }
  429.             case 1:
  430.             {
  431.                 massTrigger = true;
  432.                 type = "1";
  433.                 break;
  434.             }
  435.             case 2:
  436.             {
  437.                 type = args[0];
  438.                 target = FindUserIDByPartialName(args[1]);
  439.                 if (target == 0)
  440.                 {
  441.                     SendReply(player, GetMsg("Player: None", player));
  442.                 }
  443.                 else
  444.                 {
  445.                     massTrigger = true;
  446.                 }
  447.                 break;
  448.             }
  449.         }
  450.  
  451.         if (!massTrigger || type == null) return;
  452.         switch (type)
  453.         {
  454.             case "1":
  455.                 if (TryGetEntity<BaseEntity>(player, out var entity))
  456.                 {
  457.                     ChangeOwner(entity, target);
  458.                     SendReply(player, GetMsg("Ownership: Changed", player));
  459.                 }
  460.                 else
  461.                 {
  462.                     SendReply(player, GetMsg("Target: None", player));
  463.                 }
  464.                 break;
  465.             case "all":
  466.                 massChangeOwner<BaseEntity>(player, target);
  467.                 break;
  468.             case "block":
  469.                 massChangeOwner<BuildingBlock>(player, target);
  470.                 break;
  471.             case "storage":
  472.                 massChangeOwner<StorageContainer>(player, target);
  473.                 break;
  474.             case "sign":
  475.                 massChangeOwner<Signage>(player, target);
  476.                 break;
  477.             case "sleepingbag":
  478.                 massChangeOwner<SleepingBag>(player, target);
  479.                 break;
  480.             case "plant":
  481.                 massChangeOwner<GrowableEntity>(player, target);
  482.                 break;
  483.             case "oven":
  484.                 massChangeOwner<BaseOven>(player, target);
  485.                 break;
  486.             case "turret":
  487.                 massChangeOwner<AutoTurret>(player, target);
  488.                 break;
  489.             case "door":
  490.                 massChangeOwner<Door>(player, target);
  491.                 break;
  492.             case "cupboard":
  493.                 massChangeOwner<BuildingPrivlidge>(player, target);
  494.                 break;
  495.             case "wheel":
  496.                 massChangeOwner<SteeringWheel>(player, target);
  497.                 break;
  498.         }
  499.     }
  500.  
  501.     [ChatCommand("unown")]
  502.     private void cmdUnown(BasePlayer player, string command, string[] args)
  503.     {
  504.         if (!canChangeOwners(player))
  505.         {
  506.             SendReply(player, GetMsg("Denied: Permission", player));
  507.             return;
  508.         }
  509.  
  510.         if (args.Length == 0)
  511.         {
  512.             args = DefaultArg;
  513.         }
  514.  
  515.         if (args.Length > 1)
  516.         {
  517.             SendReply(player, GetMsg("Syntax: Unown", player));
  518.             return;
  519.         }
  520.         if (args.Length != 1) return;
  521.         switch (args[0])
  522.         {
  523.             case "1":
  524.                 if (TryGetEntity<BaseEntity>(player, out var entity))
  525.                 {
  526.                     RemoveOwner(entity);
  527.                     SendReply(player, GetMsg("Ownership: Removed", player));
  528.                 }
  529.                 else
  530.                 {
  531.                     SendReply(player, GetMsg("Target: None", player));
  532.                 }
  533.                 break;
  534.             case "all":
  535.                 massChangeOwner<BaseEntity>(player);
  536.                 break;
  537.             case "block":
  538.                 massChangeOwner<BuildingBlock>(player);
  539.                 break;
  540.             case "storage":
  541.                 massChangeOwner<StorageContainer>(player);
  542.                 break;
  543.             case "sign":
  544.                 massChangeOwner<Signage>(player);
  545.                 break;
  546.             case "sleepingbag":
  547.                 massChangeOwner<SleepingBag>(player);
  548.                 break;
  549.             case "plant":
  550.                 massChangeOwner<GrowableEntity>(player);
  551.                 break;
  552.             case "oven":
  553.                 massChangeOwner<BaseOven>(player);
  554.                 break;
  555.             case "turret":
  556.                 massChangeOwner<AutoTurret>(player);
  557.                 break;
  558.             case "door":
  559.                 massChangeOwner<Door>(player);
  560.                 break;
  561.             case "cupboard":
  562.                 massChangeOwner<BuildingPrivlidge>(player);
  563.                 break;
  564.             case "wheel":
  565.                 massChangeOwner<SteeringWheel>(player);
  566.                 break;
  567.         }
  568.     }
  569.  
  570.     [ChatCommand("auth")]
  571.     private void cmdAuth(BasePlayer player, string command, string[] args)
  572.     {
  573.         if (!canChangeOwners(player))
  574.         {
  575.             SendReply(player, GetMsg("Denied: Permission", player));
  576.             return;
  577.         }
  578.  
  579.         var massCupboard = false;
  580.         var massTurret = false;
  581.         var massWheel = false;
  582.         var checkCupboard = false;
  583.         var checkTurret = false;
  584.         var checkWheel = false;
  585.         var checkedOne = false;
  586.         BasePlayer target = null;
  587.  
  588.         switch (args.Length)
  589.         {
  590.             case 0:
  591.                 checkCupboard = checkTurret = checkWheel = true;
  592.                 break;
  593.             case 1 when args[0] == "cupboard":
  594.                 checkCupboard = true;
  595.                 break;
  596.             case 1 when args[0] == "turret":
  597.                 checkTurret = true;
  598.                 break;
  599.             case 1 when args[0] == "wheel":
  600.                 checkWheel = true;
  601.                 break;
  602.             case 1:
  603.                 massCupboard = true;
  604.                 target = FindPlayerByPartialName(args[0]);
  605.                 break;
  606.             case 2 when args[0] == "cupboard":
  607.                 massCupboard = true;
  608.                 target = FindPlayerByPartialName(args[1]);
  609.                 break;
  610.             case 2 when args[0] == "turret":
  611.                 massTurret = true;
  612.                 target = FindPlayerByPartialName(args[1]);
  613.                 break;
  614.             case 2 when args[0] == "wheel":
  615.                 massWheel = true;
  616.                 target = FindPlayerByPartialName(args[1]);
  617.                 break;
  618.             case 2:
  619.             case > 2:
  620.                 SendReply(player, GetMsg("Syntax: Auth", player));
  621.                 return;
  622.         }
  623.  
  624.         if ((massTurret || massCupboard || massWheel) && target?.net?.connection == null)
  625.         {
  626.             SendReply(player, GetMsg("Player: None", player));
  627.             return;
  628.         }
  629.  
  630.         if (massCupboard)
  631.         {
  632.             massCupboardAuthorize(player, target);
  633.         }
  634.  
  635.         if (checkCupboard &&
  636.             RaycastAll<BuildingPrivlidge>(player.eyes.HeadRay()) is BuildingPrivlidge privlidge)
  637.         {
  638.             ProdCupboard(player, privlidge);
  639.             checkedOne = true;
  640.         }
  641.  
  642.         if (massTurret)
  643.         {
  644.             massTurretAuthorize(player, target);
  645.         }
  646.  
  647.         if (checkTurret &&
  648.             RaycastAll<AutoTurret>(player.eyes.HeadRay()) is AutoTurret autoTurret)
  649.         {
  650.             ProdTurret(player, autoTurret);
  651.             checkedOne = true;
  652.         }
  653.  
  654.         if (massWheel)
  655.         {
  656.             massWheelAuthorize(player, target);
  657.         }
  658.  
  659.         if (checkWheel &&
  660.             RaycastAll<SteeringWheel>(player.eyes.HeadRay()) is SteeringWheel wheel)
  661.         {
  662.             ProdWheel(player, wheel);
  663.             checkedOne = true;
  664.         }
  665.  
  666.         if (!checkedOne && (checkCupboard || checkTurret || checkWheel))
  667.         {
  668.             SendReply(player, GetMsg("Target: None", player));
  669.         }
  670.     }
  671.  
  672.     [ChatCommand("deauth")]
  673.     private void cmdDeauth(BasePlayer player, string command, string[] args)
  674.     {
  675.         if (!canChangeOwners(player))
  676.         {
  677.             SendReply(player, GetMsg("Denied: Permission", player));
  678.             return;
  679.         }
  680.  
  681.         var massCupboard = false;
  682.         var massTurret = false;
  683.         var massWheel = false;
  684.         BasePlayer target = null;
  685.  
  686.         switch (args.Length)
  687.         {
  688.             case 0:
  689.                 SendReply(player, "Invalid Syntax. /deauth PlayerName\n/deauth turret/cupboard/wheel PlayerName");
  690.                 return;
  691.             case 1 when args[0] == "cupboard":
  692.                 SendReply(player, "Invalid Syntax. /deauth cupboard PlayerName");
  693.                 return;
  694.             case 1 when args[0] == "turret":
  695.                 SendReply(player, "Invalid Syntax. /deauth turret PlayerName");
  696.                 return;
  697.             case 1 when args[0] == "wheel":
  698.                 SendReply(player, "Invalid Syntax. /deauth wheel PlayerName");
  699.                 return;
  700.             case 1:
  701.                 massCupboard = true;
  702.                 target = FindPlayerByPartialName(args[0]);
  703.                 break;
  704.             case 2 when args[0] == "cupboard":
  705.                 massCupboard = true;
  706.                 target = FindPlayerByPartialName(args[1]);
  707.                 break;
  708.             case 2 when args[0] == "turret":
  709.                 massTurret = true;
  710.                 target = FindPlayerByPartialName(args[1]);
  711.                 break;
  712.             case 2 when args[0] == "wheel":
  713.                 massWheel = true;
  714.                 target = FindPlayerByPartialName(args[1]);
  715.                 break;
  716.             case 2:
  717.             case > 2:
  718.                 SendReply(player, GetMsg("Syntax: Deauth", player));
  719.                 return;
  720.         }
  721.  
  722.         if ((massTurret || massCupboard || massWheel) && target?.net?.connection == null)
  723.         {
  724.             SendReply(player, GetMsg("Player: None", player));
  725.             return;
  726.         }
  727.  
  728.         if (massCupboard)
  729.         {
  730.             massCupboardDeauthorize(player, target);
  731.         }
  732.  
  733.         if (massTurret)
  734.         {
  735.             massTurretDeauthorize(player, target);
  736.         }
  737.  
  738.         if (massWheel)
  739.         {
  740.             massWheelDeauthorize(player, target);
  741.         }
  742.     }
  743.  
  744.     [ChatCommand("prod2")]
  745.     private void cmdProd2(BasePlayer player, string command, string[] args)
  746.     {
  747.         if (!canCheckOwners(player))
  748.         {
  749.             SendReply(player, GetMsg("Denied: Permission", player));
  750.             return;
  751.         }
  752.  
  753.         var highlight = false;
  754.         switch (args.Length)
  755.         {
  756.             case 0:
  757.                 massProd<BaseEntity>(player);
  758.                 break;
  759.             case > 0:
  760.             {
  761.                 if (args[0] == "highlight")
  762.                 {
  763.                     highlight = true;
  764.                     args = args.Skip(1).ToArray();
  765.                 }
  766.                 if (args.Length == 0)
  767.                 {
  768.                     massProd<BaseEntity>(player, highlight);
  769.                     return;
  770.                 }
  771.  
  772.                 switch (args[0])
  773.                 {
  774.                     case "all":
  775.                         args = args.Skip(1).ToArray();
  776.                         massProd<BaseEntity>(player, highlight, args);
  777.                         break;
  778.                     case "block":
  779.                         massProd<BuildingBlock>(player, highlight);
  780.                         break;
  781.                     case "storage":
  782.                         massProd<StorageContainer>(player, highlight);
  783.                         break;
  784.                     case "sign":
  785.                         massProd<Signage>(player, highlight);
  786.                         break;
  787.                     case "sleepingbag":
  788.                         massProd<SleepingBag>(player, highlight);
  789.                         break;
  790.                     case "plant":
  791.                         massProd<GrowableEntity>(player, highlight);
  792.                         break;
  793.                     case "oven":
  794.                         massProd<BaseOven>(player, highlight);
  795.                         break;
  796.                     case "turret":
  797.                         massProdTurret(player, highlight);
  798.                         break;
  799.                     case "cupboard":
  800.                         massProdCupboard(player, highlight);
  801.                         break;
  802.                     case "wheel":
  803.                         massProdWheel(player, highlight);
  804.                         break;
  805.                     default:
  806.                         massProd<BaseEntity>(player, highlight, args);
  807.                         break;
  808.                 }
  809.  
  810.                 break;
  811.             }
  812.             default:
  813.                 SendReply(player, GetMsg("Syntax: Prod2", player));
  814.                 break;
  815.         }
  816.     }
  817.  
  818.     #endregion
  819.  
  820.     #region Permission Checks
  821.  
  822.     private bool canCheckOwners(BasePlayer player) =>
  823.         player &&
  824.         (player.net.connection.authLevel > 0 ||
  825.          permission.UserHasPermission(player.UserIDString, "entityowner.cancheckowners"));
  826.  
  827.     private bool canCheckCodes(BasePlayer player) =>
  828.         player &&
  829.         (player.net.connection.authLevel > 0 ||
  830.          permission.UserHasPermission(player.UserIDString, "entityowner.cancheckcodes"));
  831.  
  832.     private bool canCheckAssignee(BasePlayer player) =>
  833.         player &&
  834.         (player.net.connection.authLevel > 0 ||
  835.          permission.UserHasPermission(player.UserIDString, "entityowner.cancheckassignee"));
  836.  
  837.     private bool canSeeDetails(BasePlayer player) =>
  838.         player &&
  839.         (player.net.connection.authLevel > 0 ||
  840.          permission.UserHasPermission(player.UserIDString, "entityowner.seedetails"));
  841.  
  842.     private bool canChangeOwners(BasePlayer player) =>
  843.         player &&
  844.         (player.net.connection.authLevel > 0 ||
  845.          permission.UserHasPermission(player.UserIDString, "entityowner.canchangeowners"));
  846.  
  847.     #endregion
  848.  
  849.     #region Ownership Methods
  850.  
  851.     private bool TryGetEntity<T>(BasePlayer player, out BaseEntity entity) where T : BaseEntity
  852.     {
  853.         entity = null;
  854.  
  855.         var target = RaycastAll<BaseEntity>(player.eyes.HeadRay());
  856.  
  857.         if (target is not T baseEntity) return false;
  858.         entity = baseEntity;
  859.         return true;
  860.  
  861.     }
  862.  
  863.     private void massChangeOwner<T>(BasePlayer player, ulong target = 0) where T : BaseEntity
  864.     {
  865.         var entityObject = typeof(T) == typeof(BuildingBlock) ?
  866.             FindBuilding(player.transform.position, _distanceThreshold) :
  867.             FindEntity(player.transform.position, _distanceThreshold);
  868.  
  869.         switch (entityObject)
  870.         {
  871.             case bool:
  872.                 SendReply(player, GetMsg("Entities: None", player));
  873.                 return;
  874.             case null:
  875.                 return;
  876.         }
  877.  
  878.         SendReply(player, target == 0 ?
  879.             GetMsg("Ownership: Removing", player) :
  880.             GetMsg("Ownership: Changing", player));
  881.  
  882.         if (entityObject is not T entity || !entity) return;
  883.         var entityList = Pool.Get<HashSet<T>>();
  884.         var checkFrom = Pool.Get<List<Vector3>>();
  885.         entityList.Add(entity);
  886.         checkFrom.Add(entity.transform.position);
  887.         var c = 1;
  888.         if (target == 0)
  889.         {
  890.             RemoveOwner(entity);
  891.         }
  892.         else
  893.         {
  894.             ChangeOwner(entity, target);
  895.         }
  896.         var current = 0;
  897.         var bbs = 0;
  898.         var ebs = 0;
  899.         if (entity is BuildingBlock)
  900.         {
  901.             bbs++;
  902.         }
  903.         else
  904.         {
  905.             ebs++;
  906.         }
  907.         while (true)
  908.         {
  909.             current++;
  910.             if (current > _entityLimit)
  911.             {
  912.                 if (_debug)
  913.                 {
  914.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  915.                 }
  916.                 SendReply(player, string.Format(GetMsg("Entities: Count", player), c, bbs, ebs));
  917.                 break;
  918.             }
  919.             if (current > checkFrom.Count)
  920.             {
  921.                 SendReply(player, string.Format(GetMsg("Entities: Count", player), c, bbs, ebs));
  922.                 break;
  923.             }
  924.  
  925.             var hits = Pool.Get<List<T>>();
  926.             FindEntities(hits, checkFrom[current - 1], _distanceThreshold);
  927.  
  928.             foreach (var entityComponent in hits)
  929.             {
  930.                 if (!entityList.Add(entityComponent)) continue;
  931.                 c++;
  932.                 checkFrom.Add(entityComponent.transform.position);
  933.  
  934.                 if (entityComponent is BuildingBlock)
  935.                 {
  936.                     bbs++;
  937.                 }
  938.                 else
  939.                 {
  940.                     ebs++;
  941.                 }
  942.  
  943.                 if (target == 0)
  944.                 {
  945.                     RemoveOwner(entityComponent);
  946.                 }
  947.                 else
  948.                 {
  949.                     ChangeOwner(entityComponent, target);
  950.                 }
  951.             }
  952.             Pool.FreeUnmanaged(ref hits);
  953.         }
  954.         Pool.FreeUnmanaged(ref checkFrom);
  955.         Pool.FreeUnmanaged(ref entityList);
  956.  
  957.         if (target == 0)
  958.         {
  959.             SendReply(player, string.Format(GetMsg("Ownership: New", player), "No one"));
  960.             return;
  961.         }
  962.  
  963.         var targetPlayer = BasePlayer.FindByID(target);
  964.         if (targetPlayer)
  965.         {
  966.             SendReply(player, string.Format(GetMsg("Ownership: New", player), targetPlayer.displayName));
  967.             SendReply(targetPlayer, GetMsg("Ownership: New Self", player));
  968.             return;
  969.         }
  970.  
  971.         var pl = covalence.Players.FindPlayerById(target.ToString());
  972.         SendReply(player, string.Format(GetMsg("Target: Owner", player), pl.Name));
  973.     }
  974.  
  975.     private void massProd<T>(BasePlayer player, bool highlight = false, params string[] filter) where T : BaseEntity
  976.     {
  977.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  978.         if (entityObject is bool)
  979.         {
  980.             SendReply(player, GetMsg("Entities: None", player));
  981.             return;
  982.         }
  983.  
  984.         var health = 0f;
  985.         var maxHealth = 0f;
  986.         var prodOwners = Pool.Get<Dictionary<ulong, int>>();
  987.         if (entityObject is not BaseEntity entity || !entity.transform)
  988.         {
  989.             SendReply(player, GetMsg("Entities: None", player));
  990.             return;
  991.         }
  992.  
  993.         SendReply(player, GetMsg("Structure: Prodding", player));
  994.  
  995.         var entityList = Pool.Get<HashSet<T>>();
  996.         var checkFrom = Pool.Get<List<Vector3>>();
  997.  
  998.         if (entity is T baseEntity)
  999.         {
  1000.             entityList.Add(baseEntity);
  1001.         }
  1002.  
  1003.         var total = 0;
  1004.         var skip = false;
  1005.         if (entity is T)
  1006.         {
  1007.             if (filter.Length > 0)
  1008.             {
  1009.                 skip = true;
  1010.                 foreach (var f in filter)
  1011.                 {
  1012.                     if (!entity.name.ToLower().Contains(f.ToLower())) continue;
  1013.                     skip = false;
  1014.                     break;
  1015.                 }
  1016.             }
  1017.  
  1018.             if (!skip)
  1019.             {
  1020.                 prodOwners.Add(entity.OwnerID, 1);
  1021.                 health += entity.Health();
  1022.                 maxHealth += entity.MaxHealth();
  1023.                 total++;
  1024.             }
  1025.         }
  1026.  
  1027.         var current = -1;
  1028.         var distanceThreshold = _distanceThreshold;
  1029.         if (typeof(T) != typeof(BuildingBlock) && typeof(T) != typeof(BaseEntity))
  1030.             distanceThreshold += 30;
  1031.  
  1032.         var hits = Pool.Get<List<T>>();
  1033.         while (true)
  1034.         {
  1035.             current++;
  1036.             if (current > _entityLimit)
  1037.             {
  1038.                 SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1039.  
  1040.                 break;
  1041.             }
  1042.             if (current > checkFrom.Count)
  1043.             {
  1044.                 break;
  1045.             }
  1046.  
  1047.             hits.Clear();
  1048.             FindEntities(hits, checkFrom.Count > 0 ? checkFrom[current - 1] : entity.transform.position, distanceThreshold);
  1049.             skip = false;
  1050.             foreach (var fentity in hits)
  1051.             {
  1052.                 // BasePlayer check replaces the following check, which wasn't
  1053.                 //  doing anything except allocating massive amounts of memory:
  1054.                 //   || fentity.name == "player/player")
  1055.                 if (!fentity.transform || !entityList.Add(fentity) || fentity is BasePlayer)
  1056.                     continue;
  1057.  
  1058.                 if (filter.Length > 0)
  1059.                 {
  1060.                     skip = true;
  1061.                     foreach (var f in filter)
  1062.                     {
  1063.                         if (!fentity.name.ToLower().Contains(f.ToLower())) continue;
  1064.                         skip = false;
  1065.                         break;
  1066.                     }
  1067.                 }
  1068.  
  1069.                 checkFrom.Add(fentity.transform.position);
  1070.  
  1071.                 if (skip) continue;
  1072.                 total++;
  1073.                 if (highlight)
  1074.                 {
  1075.                     SendHighlight(player, fentity.transform.position);
  1076.                 }
  1077.  
  1078.                 var pid = fentity.OwnerID;
  1079.                 if (!prodOwners.TryAdd(pid, 1))
  1080.                 {
  1081.                     prodOwners[pid]++;
  1082.                 }
  1083.  
  1084.                 health += fentity.Health();
  1085.                 maxHealth += fentity.MaxHealth();
  1086.             }
  1087.         }
  1088.         hits.Clear();
  1089.         Pool.FreeUnmanaged(ref hits);
  1090.         Pool.FreeUnmanaged(ref checkFrom);
  1091.         Pool.FreeUnmanaged(ref entityList);
  1092.  
  1093.         var unknown = 100;
  1094.  
  1095.         var sb = Pool.Get<StringBuilder>();
  1096.         sb.AppendLine("<size=16>Structure</size>");
  1097.         sb.Append("Entities: ").Append(total).AppendLine();
  1098.  
  1099.         if (health > 0 && maxHealth > 0)
  1100.         {
  1101.             var condition = Mathf.Round(health * 100 / maxHealth);
  1102.             sb.AppendFormat(GetMsg("Structure: Condition Percent", player), condition);
  1103.         }
  1104.  
  1105.         SendReply(player, sb.ToString());
  1106.         sb.Clear();
  1107.  
  1108.         sb.AppendLine("<size=16>Ownership</size>");
  1109.  
  1110.         if (total > 0)
  1111.         {
  1112.             foreach (var kvp in prodOwners)
  1113.             {
  1114.                 var perc = kvp.Value * 100 / total;
  1115.                 if (kvp.Key == 0) continue;
  1116.                 var n = FindPlayerName(kvp.Key);
  1117.                 sb.Append(n).Append(": ").Append(perc).Append('%').AppendLine();
  1118.                 unknown -= perc;
  1119.             }
  1120.         }
  1121.         Pool.FreeUnmanaged(ref prodOwners);
  1122.  
  1123.         if (unknown > 0)
  1124.             sb.AppendFormat(GetMsg("Player: Unknown Percent", player), unknown);
  1125.  
  1126.         SendReply(player, sb.ToString());
  1127.  
  1128.         Pool.FreeUnmanaged(ref sb);
  1129.     }
  1130.  
  1131.     private static void SendHighlight(BasePlayer player, Vector3 position)
  1132.     {
  1133.         player.SendConsoleCommand("ddraw.sphere", 30f, Color.magenta, position, 2f);
  1134.         player.SendNetworkUpdateImmediate();
  1135.     }
  1136.  
  1137.     private void ProdCupboard(BasePlayer player, BuildingPrivlidge cupboard)
  1138.     {
  1139.         var sb = Pool.Get<StringBuilder>();
  1140.         var authorizedUsers = Pool.Get<List<string>>();
  1141.         if (TryGetCupboardUserNames(authorizedUsers, cupboard))
  1142.         {
  1143.             sb.AppendLine(string.Format(GetMsg("Entities: Authorized", player), authorizedUsers.Count));
  1144.             foreach (var n in authorizedUsers)
  1145.                 sb.AppendLine(n);
  1146.         }
  1147.         else
  1148.         {
  1149.             sb.Append(string.Format(GetMsg("Target: None", player)));
  1150.         }
  1151.         Pool.FreeUnmanaged(ref authorizedUsers);
  1152.  
  1153.         SendReply(player, sb.ToString());
  1154.         Pool.FreeUnmanaged(ref sb);
  1155.     }
  1156.  
  1157.     private void ProdTurret(BasePlayer player, AutoTurret turret)
  1158.     {
  1159.         var sb = Pool.Get<StringBuilder>();
  1160.         var authorizedUsers = Pool.Get<List<string>>();
  1161.         if (TryGetTurretUserNames(authorizedUsers, turret))
  1162.         {
  1163.             sb.AppendLine(string.Format(GetMsg("Entities: Authorized", player), authorizedUsers.Count));
  1164.             foreach (var n in authorizedUsers)
  1165.                 sb.AppendLine(n);
  1166.         }
  1167.         else
  1168.         {
  1169.             sb.Append(string.Format(GetMsg("Target: None", player)));
  1170.         }
  1171.         Pool.FreeUnmanaged(ref authorizedUsers);
  1172.  
  1173.         SendReply(player, sb.ToString());
  1174.         Pool.FreeUnmanaged(ref sb);
  1175.     }
  1176.  
  1177.     private void ProdWheel(BasePlayer player, SteeringWheel wheel)
  1178.     {
  1179.         var sb = Pool.Get<StringBuilder>();
  1180.         var authorizedUsers = Pool.Get<List<string>>();
  1181.         if (TryGetWheelUserNames(authorizedUsers, wheel))
  1182.         {
  1183.             sb.AppendLine(string.Format(GetMsg("Entities: Authorized", player), authorizedUsers.Count));
  1184.             foreach (var n in authorizedUsers)
  1185.                 sb.AppendLine(n);
  1186.         }
  1187.         else
  1188.         {
  1189.             sb.Append(string.Format(GetMsg("Target: None", player)));
  1190.         }
  1191.         Pool.FreeUnmanaged(ref authorizedUsers);
  1192.  
  1193.         SendReply(player, sb.ToString());
  1194.         Pool.FreeUnmanaged(ref sb);
  1195.     }
  1196.  
  1197.     private void massProdCupboard(BasePlayer player, bool highlight = false)
  1198.     {
  1199.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1200.  
  1201.         if (entityObject is bool)
  1202.         {
  1203.             SendReply(player, GetMsg("Entities: None", player));
  1204.             return;
  1205.         }
  1206.  
  1207.         if (entityObject is not BaseEntity entity || !entity) return;
  1208.         var total = 0;
  1209.         var prodOwners = Pool.Get<Dictionary<ulong, int>>();
  1210.         SendReply(player, GetMsg("Cupboards: Prodding", player));
  1211.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1212.         var checkFrom = Pool.Get<List<Vector3>>();
  1213.         checkFrom.Add(entity.transform.position);
  1214.  
  1215.         var current = 0;
  1216.         while (true)
  1217.         {
  1218.             current++;
  1219.             if (current > _entityLimit)
  1220.             {
  1221.                 if (_debug)
  1222.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1223.  
  1224.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1225.                 break;
  1226.             }
  1227.             if (current > checkFrom.Count)
  1228.             {
  1229.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1230.                 break;
  1231.             }
  1232.  
  1233.             var entities = Pool.Get<List<BuildingPrivlidge>>();
  1234.             FindEntities(entities, checkFrom[current - 1], _cupboardDistanceThreshold);
  1235.  
  1236.             foreach (var e in entities)
  1237.             {
  1238.                 if (!entityList.Add(e)) { continue; }
  1239.                 if (highlight)
  1240.                 {
  1241.                     SendHighlight(player, e.transform.position);
  1242.                 }
  1243.                 checkFrom.Add(e.transform.position);
  1244.  
  1245.                 foreach (var userid in e.authorizedPlayers)
  1246.                 {
  1247.                     if (!prodOwners.TryAdd(userid, 1))
  1248.                         prodOwners[userid]++;
  1249.                 }
  1250.  
  1251.                 total++;
  1252.             }
  1253.             Pool.FreeUnmanaged(ref entities);
  1254.         }
  1255.         Pool.FreeUnmanaged(ref checkFrom);
  1256.         Pool.FreeUnmanaged(ref entityList);
  1257.  
  1258.         // var percs = new Dictionary<ulong, int>();
  1259.         var unknown = 100;
  1260.         if (total <= 0) return;
  1261.         foreach (var kvp in prodOwners)
  1262.         {
  1263.             var perc = kvp.Value * 100 / total;
  1264.             // percs.Add(kvp.Key, perc);
  1265.             var n = FindPlayerName(kvp.Key);
  1266.  
  1267.             if (n.Contains("Unknown: ")) continue;
  1268.             SendReply(player, n + ": " + perc + "%");
  1269.             unknown -= perc;
  1270.         }
  1271.         Pool.FreeUnmanaged(ref prodOwners);
  1272.  
  1273.         if (unknown > 0)
  1274.             SendReply(player, string.Format(GetMsg("Player: Unknown Percent", player), unknown));
  1275.     }
  1276.  
  1277.     private void massProdTurret(BasePlayer player, bool highlight = false)
  1278.     {
  1279.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1280.  
  1281.         if (entityObject is bool)
  1282.         {
  1283.             SendReply(player, GetMsg("Entities: None", player));
  1284.             return;
  1285.         }
  1286.  
  1287.         if (entityObject is not BaseEntity entity || !entity) return;
  1288.         var total = 0;
  1289.         var prodOwners = Pool.Get<Dictionary<ulong, int>>();
  1290.         SendReply(player, GetMsg("Turrets: Prodding", player));
  1291.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1292.         var checkFrom = Pool.Get<List<Vector3>>();
  1293.         checkFrom.Add(entity.transform.position);
  1294.  
  1295.         var current = 0;
  1296.         while (true)
  1297.         {
  1298.             current++;
  1299.             if (current > _entityLimit)
  1300.             {
  1301.                 if (_debug)
  1302.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1303.  
  1304.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1305.                 break;
  1306.             }
  1307.             if (current > checkFrom.Count)
  1308.             {
  1309.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1310.                 break;
  1311.             }
  1312.  
  1313.             var entities = Pool.Get<List<BaseEntity>>();
  1314.             FindEntities(entities, checkFrom[current - 1], _distanceThreshold);
  1315.  
  1316.             foreach (var e in entities)
  1317.             {
  1318.                 if (!entityList.Add(e)) { continue; }
  1319.                 if (highlight)
  1320.                 {
  1321.                     SendHighlight(player, e.transform.position);
  1322.                 }
  1323.                 checkFrom.Add(e.transform.position);
  1324.  
  1325.                 switch (e)
  1326.                 {
  1327.                     case AutoTurret autoTurret:
  1328.                     {
  1329.                         if (autoTurret.OwnerID.IsSteamId() &&
  1330.                             !prodOwners.TryAdd(autoTurret.OwnerID, 1))
  1331.                         {
  1332.                             prodOwners[autoTurret.OwnerID]++;
  1333.                         }
  1334.  
  1335.                         foreach (var userid in autoTurret.authorizedPlayers)
  1336.                         {
  1337.                             if (!prodOwners.TryAdd(userid, 1))
  1338.                                 prodOwners[userid]++;
  1339.                         }
  1340.  
  1341.                         break;
  1342.                     }
  1343.                     case FlameTurret flameTurret:
  1344.                     {
  1345.                         if (flameTurret.OwnerID.IsSteamId() &&
  1346.                             !prodOwners.TryAdd(flameTurret.OwnerID, 1))
  1347.                         {
  1348.                             prodOwners[flameTurret.OwnerID]++;
  1349.                         }
  1350.  
  1351.                         break;
  1352.                     }
  1353.                     default:
  1354.                         continue;
  1355.                 }
  1356.  
  1357.                 total++;
  1358.             }
  1359.  
  1360.             Pool.FreeUnmanaged(ref entities);
  1361.         }
  1362.         Pool.FreeUnmanaged(ref checkFrom);
  1363.         Pool.FreeUnmanaged(ref entityList);
  1364.  
  1365.         // var percs = new Dictionary<ulong, int>();
  1366.         var unknown = 100;
  1367.         if (total <= 0) return;
  1368.         foreach (var kvp in prodOwners)
  1369.         {
  1370.             var perc = kvp.Value * 100 / total;
  1371.             // percs.Add(kvp.Key, perc);
  1372.             var n = FindPlayerName(kvp.Key);
  1373.  
  1374.             if (n.Contains("Unknown: ")) continue;
  1375.             SendReply(player, n + ": " + perc + "%");
  1376.             unknown -= perc;
  1377.         }
  1378.         Pool.FreeUnmanaged(ref prodOwners);
  1379.  
  1380.         if (unknown > 0)
  1381.             SendReply(player, string.Format(GetMsg("Player: Unknown Percent", player), unknown));
  1382.     }
  1383.  
  1384.     private void massProdWheel(BasePlayer player, bool highlight = false)
  1385.     {
  1386.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1387.  
  1388.         if (entityObject is bool)
  1389.         {
  1390.             SendReply(player, GetMsg("Entities: None", player));
  1391.             return;
  1392.         }
  1393.  
  1394.         if (entityObject is not BaseEntity entity || !entity) return;
  1395.         var total = 0;
  1396.         var prodOwners = Pool.Get<Dictionary<ulong, int>>();
  1397.         SendReply(player, GetMsg("Wheels: Prodding", player));
  1398.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1399.         var checkFrom = Pool.Get<List<Vector3>>();
  1400.         checkFrom.Add(entity.transform.position);
  1401.  
  1402.         var current = 0;
  1403.         while (true)
  1404.         {
  1405.             current++;
  1406.             if (current > _entityLimit)
  1407.             {
  1408.                 if (_debug)
  1409.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1410.  
  1411.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1412.                 break;
  1413.             }
  1414.             if (current > checkFrom.Count)
  1415.             {
  1416.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1417.                 break;
  1418.             }
  1419.  
  1420.             var entities = Pool.Get<List<SteeringWheel>>();
  1421.             FindEntities(entities, checkFrom[current - 1], _wheelDistanceThreshold);
  1422.  
  1423.             foreach (var e in entities)
  1424.             {
  1425.                 if (null == e?.Privilege?.authorizedPlayers) continue;
  1426.                 if (!entityList.Add(e)) { continue; }
  1427.                 if (highlight)
  1428.                 {
  1429.                     SendHighlight(player, e.transform.position);
  1430.                 }
  1431.                 checkFrom.Add(e.transform.position);
  1432.  
  1433.                 foreach (var userid in e.Privilege.authorizedPlayers)
  1434.                 {
  1435.                     if (!prodOwners.TryAdd(userid, 1))
  1436.                         prodOwners[userid]++;
  1437.                 }
  1438.  
  1439.                 total++;
  1440.             }
  1441.             Pool.FreeUnmanaged(ref entities);
  1442.         }
  1443.         Pool.FreeUnmanaged(ref checkFrom);
  1444.         Pool.FreeUnmanaged(ref entityList);
  1445.  
  1446.         // var percs = new Dictionary<ulong, int>();
  1447.         var unknown = 100;
  1448.         if (total <= 0) return;
  1449.         foreach (var kvp in prodOwners)
  1450.         {
  1451.             var perc = kvp.Value * 100 / total;
  1452.             // percs.Add(kvp.Key, perc);
  1453.             var n = FindPlayerName(kvp.Key);
  1454.  
  1455.             if (n.Contains("Unknown: ")) continue;
  1456.             SendReply(player, n + ": " + perc + "%");
  1457.             unknown -= perc;
  1458.         }
  1459.         Pool.FreeUnmanaged(ref prodOwners);
  1460.  
  1461.         if (unknown > 0)
  1462.             SendReply(player, string.Format(GetMsg("Player: Unknown Percent", player), unknown));
  1463.     }
  1464.  
  1465.     private void massCupboardAuthorize(BasePlayer player, BasePlayer target)
  1466.     {
  1467.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1468.  
  1469.         if (entityObject is bool)
  1470.         {
  1471.             SendReply(player, GetMsg("Entities: None", player));
  1472.             return;
  1473.         }
  1474.  
  1475.         if (entityObject is not BaseEntity entity || !entity) return;
  1476.         var total = 0;
  1477.         SendReply(player, GetMsg("Cupboards: Authorizing", player));
  1478.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1479.         var checkFrom = Pool.Get<List<Vector3>>();
  1480.         checkFrom.Add(entity.transform.position);
  1481.  
  1482.         var current = 0;
  1483.         while (true)
  1484.         {
  1485.             current++;
  1486.             if (current > _entityLimit)
  1487.             {
  1488.                 if (_debug)
  1489.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1490.  
  1491.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1492.                 break;
  1493.             }
  1494.             if (current > checkFrom.Count)
  1495.             {
  1496.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1497.                 break;
  1498.             }
  1499.  
  1500.             var entities = Pool.Get<List<BuildingPrivlidge>>();
  1501.             FindEntities(entities, checkFrom[current - 1], _cupboardDistanceThreshold);
  1502.  
  1503.             foreach (var priv in entities)
  1504.             {
  1505.                 if (!entityList.Add(priv)) continue;
  1506.                 checkFrom.Add(priv.transform.position);
  1507.                 if (HasCupboardAccess(priv, target)) continue;
  1508.                 priv.authorizedPlayers.Add(target.userID);
  1509.  
  1510.                 priv.SendNetworkUpdate();
  1511.  
  1512.                 total++;
  1513.             }
  1514.             Pool.FreeUnmanaged(ref entities);
  1515.         }
  1516.         Pool.FreeUnmanaged(ref checkFrom);
  1517.         Pool.FreeUnmanaged(ref entityList);
  1518.  
  1519.         SendReply(player, string.Format(GetMsg("Cupboards: Authorized", player), target.displayName, total));
  1520.     }
  1521.  
  1522.     private void massCupboardDeauthorize(BasePlayer player, BasePlayer target)
  1523.     {
  1524.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1525.  
  1526.         if (entityObject is bool)
  1527.         {
  1528.             SendReply(player, GetMsg("Entities: None", player));
  1529.             return;
  1530.         }
  1531.  
  1532.         if (entityObject is not BaseEntity entity || !entity) return;
  1533.         var total = 0;
  1534.         SendReply(player, GetMsg("Cupboards: Deauthorizing", player));
  1535.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1536.         var checkFrom = Pool.Get<List<Vector3>>();
  1537.         checkFrom.Add(entity.transform.position);
  1538.  
  1539.         var current = 0;
  1540.         while (true)
  1541.         {
  1542.             current++;
  1543.             if (current > _entityLimit)
  1544.             {
  1545.                 if (_debug)
  1546.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1547.  
  1548.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1549.                 break;
  1550.             }
  1551.             if (current > checkFrom.Count)
  1552.             {
  1553.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1554.                 break;
  1555.             }
  1556.  
  1557.             var entities = Pool.Get<List<BuildingPrivlidge>>();
  1558.             FindEntities(entities, checkFrom[current - 1], _cupboardDistanceThreshold);
  1559.  
  1560.             foreach (var priv in entities)
  1561.             {
  1562.                 if (!entityList.Add(priv)) continue;
  1563.                 checkFrom.Add(priv.transform.position);
  1564.  
  1565.                 if (!HasCupboardAccess(priv, target)) continue;
  1566.                 if( priv.authorizedPlayers.Remove( target.userID ) )
  1567.                 {
  1568.                     priv.SendNetworkUpdate();
  1569.                 }
  1570.  
  1571.                 total++;
  1572.             }
  1573.             Pool.FreeUnmanaged(ref entities);
  1574.         }
  1575.         Pool.FreeUnmanaged(ref checkFrom);
  1576.         Pool.FreeUnmanaged(ref entityList);
  1577.  
  1578.         SendReply(player, string.Format(GetMsg("Cupboards: Deauthorized", player), target.displayName, total));
  1579.     }
  1580.  
  1581.     private void massTurretAuthorize(BasePlayer player, BasePlayer target)
  1582.     {
  1583.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1584.  
  1585.         if (entityObject is bool)
  1586.         {
  1587.             SendReply(player, GetMsg("Entities: None", player));
  1588.             return;
  1589.         }
  1590.  
  1591.         if (entityObject is not BaseEntity entity || !entity) return;
  1592.         var total = 0;
  1593.         SendReply(player, GetMsg("Turrets: Authorizing", player));
  1594.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1595.         var checkFrom = Pool.Get<List<Vector3>>();
  1596.         checkFrom.Add(entity.transform.position);
  1597.  
  1598.         var current = 0;
  1599.         while (true)
  1600.         {
  1601.             current++;
  1602.             if (current > _entityLimit)
  1603.             {
  1604.                 if (_debug)
  1605.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1606.  
  1607.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1608.                 break;
  1609.             }
  1610.             if (current > checkFrom.Count)
  1611.             {
  1612.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1613.                 break;
  1614.             }
  1615.  
  1616.             var entities = Pool.Get<List<BaseEntity>>();
  1617.             FindEntities(entities, checkFrom[current - 1], _distanceThreshold);
  1618.  
  1619.             foreach (var e in entities)
  1620.             {
  1621.                 if (!entityList.Add(e)) continue;
  1622.                 checkFrom.Add(e.transform.position);
  1623.  
  1624.                 if (e is not AutoTurret turret || !turret || HasTurretAccess(turret, target)) continue;
  1625.                 turret.authorizedPlayers.Add(target.userID);
  1626.  
  1627.                 turret.SendNetworkUpdate();
  1628.                 turret.SetTarget(null);
  1629.                 total++;
  1630.             }
  1631.             Pool.FreeUnmanaged(ref entities);
  1632.         }
  1633.         Pool.FreeUnmanaged(ref checkFrom);
  1634.         Pool.FreeUnmanaged(ref entityList);
  1635.  
  1636.         SendReply(player, string.Format(GetMsg("Turrets: Authorized", player), target.displayName, total));
  1637.     }
  1638.  
  1639.     private void massTurretDeauthorize(BasePlayer player, BasePlayer target)
  1640.     {
  1641.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1642.  
  1643.         if (entityObject is bool)
  1644.         {
  1645.             SendReply(player, GetMsg("Entities: None", player));
  1646.             return;
  1647.         }
  1648.  
  1649.         if (entityObject is not BaseEntity entity || !entity) return;
  1650.         var total = 0;
  1651.         SendReply(player, GetMsg("Turrets: Deauthorizing", player));
  1652.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1653.         var checkFrom = Pool.Get<List<Vector3>>();
  1654.         checkFrom.Add(entity.transform.position);
  1655.  
  1656.         var current = 0;
  1657.         while (true)
  1658.         {
  1659.             current++;
  1660.             if (current > _entityLimit)
  1661.             {
  1662.                 if (_debug)
  1663.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1664.  
  1665.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1666.                 break;
  1667.             }
  1668.             if (current > checkFrom.Count)
  1669.             {
  1670.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1671.                 break;
  1672.             }
  1673.  
  1674.             var entities = Pool.Get<List<BaseEntity>>();
  1675.             FindEntities(entities, checkFrom[current - 1], _distanceThreshold);
  1676.  
  1677.             foreach (var e in entities)
  1678.             {
  1679.                 if (!entityList.Add(e)) continue;
  1680.                 checkFrom.Add(e.transform.position);
  1681.  
  1682.                 if (e is not AutoTurret turret || !turret || !HasTurretAccess(turret, target)) continue;
  1683.                 if (turret.authorizedPlayers.Remove(target.userID))
  1684.                 {
  1685.                     turret.SetTarget(null);
  1686.                     total++;
  1687.                 }
  1688.  
  1689.                 turret.SendNetworkUpdate();
  1690.             }
  1691.             Pool.FreeUnmanaged(ref entities);
  1692.         }
  1693.         Pool.FreeUnmanaged(ref checkFrom);
  1694.         Pool.FreeUnmanaged(ref entityList);
  1695.  
  1696.         SendReply(player, string.Format(GetMsg("Turrets: Deauthorized", player), target.displayName, total));
  1697.     }
  1698.  
  1699.     private void massWheelAuthorize(BasePlayer player, BasePlayer target)
  1700.     {
  1701.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1702.  
  1703.         if (entityObject is bool)
  1704.         {
  1705.             SendReply(player, GetMsg("Entities: None", player));
  1706.             return;
  1707.         }
  1708.  
  1709.         if (entityObject is not BaseEntity entity || !entity) return;
  1710.         var total = 0;
  1711.         SendReply(player, GetMsg("Wheels: Authorizing", player));
  1712.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1713.         var checkFrom = Pool.Get<List<Vector3>>();
  1714.         checkFrom.Add(entity.transform.position);
  1715.  
  1716.         var current = 0;
  1717.         while (true)
  1718.         {
  1719.             current++;
  1720.             if (current > _entityLimit)
  1721.             {
  1722.                 if (_debug)
  1723.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1724.  
  1725.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1726.                 break;
  1727.             }
  1728.             if (current > checkFrom.Count)
  1729.             {
  1730.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1731.                 break;
  1732.             }
  1733.  
  1734.             var entities = Pool.Get<List<SteeringWheel>>();
  1735.             FindEntities(entities, checkFrom[current - 1], _wheelDistanceThreshold);
  1736.  
  1737.             foreach (var wheel in entities)
  1738.             {
  1739.                 if (!entityList.Add(wheel)) continue;
  1740.                 checkFrom.Add(wheel.transform.position);
  1741.                 if (HasWheelAccess(wheel, target)) continue;
  1742.                 wheel.Privilege?.authorizedPlayers?.Add(target.userID);
  1743.                 wheel.SendNetworkUpdate();
  1744.                 total++;
  1745.             }
  1746.             Pool.FreeUnmanaged(ref entities);
  1747.         }
  1748.         Pool.FreeUnmanaged(ref checkFrom);
  1749.         Pool.FreeUnmanaged(ref entityList);
  1750.  
  1751.         SendReply(player, string.Format(GetMsg("Wheels: Authorized", player), target.displayName, total));
  1752.     }
  1753.  
  1754.     private void massWheelDeauthorize(BasePlayer player, BasePlayer target)
  1755.     {
  1756.         var entityObject = FindEntity(player.transform.position, _distanceThreshold);
  1757.  
  1758.         if (entityObject is bool)
  1759.         {
  1760.             SendReply(player, GetMsg("Entities: None", player));
  1761.             return;
  1762.         }
  1763.  
  1764.         if (entityObject is not BaseEntity entity || !entity) return;
  1765.         var total = 0;
  1766.         SendReply(player, GetMsg("Wheels: Deauthorizing", player));
  1767.         var entityList = Pool.Get<HashSet<BaseEntity>>();
  1768.         var checkFrom = Pool.Get<List<Vector3>>();
  1769.         checkFrom.Add(entity.transform.position);
  1770.  
  1771.         var current = 0;
  1772.         while (true)
  1773.         {
  1774.             current++;
  1775.             if (current > _entityLimit)
  1776.             {
  1777.                 if (_debug)
  1778.                     SendReply(player, GetMsg("Target: Limit", player) + " " + _entityLimit);
  1779.  
  1780.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1781.                 break;
  1782.             }
  1783.             if (current > checkFrom.Count)
  1784.             {
  1785.                 SendReply(player, string.Format(GetMsg("Ownership: Count", player), total));
  1786.                 break;
  1787.             }
  1788.  
  1789.             var entities = Pool.Get<List<SteeringWheel>>();
  1790.             FindEntities(entities, checkFrom[current - 1], _wheelDistanceThreshold);
  1791.  
  1792.             foreach (var wheel in entities)
  1793.             {
  1794.                 if (!entityList.Add(wheel)) continue;
  1795.                 checkFrom.Add(wheel.transform.position);
  1796.                 if (!HasWheelAccess(wheel, target)) continue;
  1797.                 if (true == wheel.Privilege?.authorizedPlayers?.Remove(target.userID))
  1798.                 {
  1799.                     wheel.SendNetworkUpdate();
  1800.                 }
  1801.                 total++;
  1802.             }
  1803.             Pool.FreeUnmanaged(ref entities);
  1804.         }
  1805.         Pool.FreeUnmanaged(ref checkFrom);
  1806.         Pool.FreeUnmanaged(ref entityList);
  1807.  
  1808.         SendReply(player, string.Format(GetMsg("Wheels: Deauthorized", player), target.displayName, total));
  1809.     }
  1810.  
  1811.     private bool TryGetCupboardUserNames(List<string> names, BuildingPrivlidge cupboard)
  1812.     {
  1813.         if (cupboard?.authorizedPlayers is not { Count: not 0 })
  1814.             return false;
  1815.  
  1816.         foreach (var userid in cupboard.authorizedPlayers)
  1817.             names.Add($"{FindPlayerName(userid)} - {userid}");
  1818.  
  1819.         return true;
  1820.     }
  1821.  
  1822.     private bool TryGetTurretUserNames(List<string> names, AutoTurret turret)
  1823.     {
  1824.         if (turret?.authorizedPlayers is not { Count: not 0 })
  1825.             return false;
  1826.  
  1827.         foreach (var userid in turret.authorizedPlayers)
  1828.             names.Add($"{FindPlayerName(userid)} - {userid}");
  1829.  
  1830.         return true;
  1831.     }
  1832.  
  1833.     private bool TryGetWheelUserNames(List<string> names, SteeringWheel wheel)
  1834.     {
  1835.         if (wheel?.Privilege?.authorizedPlayers is not { Count: not 0 })
  1836.             return false;
  1837.  
  1838.         foreach (var userid in wheel.Privilege.authorizedPlayers)
  1839.             names.Add($"{FindPlayerName(userid)} - {userid}");
  1840.  
  1841.         return true;
  1842.     }
  1843.  
  1844.     private static bool HasCupboardAccess(BuildingPrivlidge cupboard, BasePlayer player) =>
  1845.         true == cupboard?.IsAuthed(player);
  1846.  
  1847.     private static bool HasTurretAccess(AutoTurret turret, BasePlayer player) =>
  1848.         true == turret?.IsAuthed(player);
  1849.  
  1850.     private static bool HasWheelAccess(SteeringWheel wheel, BasePlayer player) =>
  1851.         true == wheel?.IsAuthed(player);
  1852.  
  1853.     private string GetOwnerName(BaseEntity entity) =>
  1854.         entity is not null && entity ? FindPlayerName(entity.OwnerID) : "Unknown: <null>";
  1855.  
  1856.     // private static BasePlayer GetOwnerPlayer(BaseEntity entity) =>
  1857.     //     entity.OwnerID.IsSteamId() ? BasePlayer.FindByID(entity.OwnerID) : null;
  1858.     //
  1859.     // private IPlayer GetOwnerIPlayer(BaseEntity entity) =>
  1860.     //     entity.OwnerID.IsSteamId() ?
  1861.     //         covalence.Players.FindPlayerById(entity.OwnerID.ToString()) : null;
  1862.  
  1863.     private static void RemoveOwner(BaseEntity entity)
  1864.     {
  1865.         entity.OwnerID = 0;
  1866.     }
  1867.  
  1868.     private void ChangeOwner(BaseEntity entity, object player)
  1869.     {
  1870.         //get the current owner for debug log
  1871.         var oldOwner = GetOwnerDisplayName(entity);
  1872.         if (string.IsNullOrEmpty(oldOwner))
  1873.         {
  1874.             oldOwner = "N/A";
  1875.         }
  1876.  
  1877.         switch (player)
  1878.         {
  1879.             case BasePlayer basePlayer:
  1880.                 entity.OwnerID = basePlayer.userID;
  1881.                 break;
  1882.             case IPlayer iPlayer:
  1883.                 entity.OwnerID = Convert.ToUInt64(iPlayer.Id);
  1884.                 break;
  1885.             case ulong playerID when playerID.IsSteamId():
  1886.                 entity.OwnerID = playerID;
  1887.                 break;
  1888.             case string playerString:
  1889.             {
  1890.                 var basePlayer = BasePlayer.Find(playerString);
  1891.                 if (ulong.TryParse(playerString, out var id) && id.IsSteamId())
  1892.                     entity.OwnerID = id;
  1893.                 else if (basePlayer)
  1894.                     entity.OwnerID = basePlayer.userID;
  1895.                 break;
  1896.             }
  1897.         }
  1898.  
  1899.         //get the current owner for the debug log
  1900.         var newOwner = GetOwnerDisplayName(entity);
  1901.         if (string.IsNullOrEmpty(newOwner))
  1902.         {
  1903.             newOwner = "N/A";
  1904.         }
  1905.  
  1906.         //Debug
  1907.         if(_debug)
  1908.             Puts("Changed owner of [" + entity.ShortPrefabName + "] from [" + oldOwner + "] to [" + newOwner + "]");
  1909.     }
  1910.  
  1911.     // private static object FindEntityData(BaseEntity entity) =>
  1912.     //     entity.OwnerID.IsSteamId() ? entity.OwnerID.ToString() : FalseObject;
  1913.  
  1914.     #endregion
  1915.  
  1916.     #region Utility Methods
  1917.  
  1918.     // private object RaycastAll<T>(Vector3 position, Vector3 aim) where T : BaseEntity
  1919.     // {
  1920.     //     var hitCount = Physics.RaycastNonAlloc(position, aim, _raycastHits);
  1921.     //     if (hitCount >= RaycastHitsMax)
  1922.     //     {
  1923.     //         PrintWarning($"Got maximum number of raycast hits ({RaycastHitsMax}); report to developer");
  1924.     //     }
  1925.     //     const float distance = 100f;
  1926.     //     object target = false;
  1927.     //     for (var i = 0; i < hitCount; ++i)
  1928.     //     {
  1929.     //         var hit = _raycastHits[i];
  1930.     //         var ent = hit.GetEntity();
  1931.     //         if (ent is not T || hit.distance >= distance) continue;
  1932.     //         target = ent;
  1933.     //         break;
  1934.     //     }
  1935.     //     return target;
  1936.     // }
  1937.  
  1938.     private object RaycastAll<T>(Ray ray) where T : BaseEntity
  1939.     {
  1940.         var hitCount = Physics.RaycastNonAlloc(ray, _raycastHits, 100f);
  1941.         if (hitCount >= RaycastHitsMax)
  1942.         {
  1943.             PrintWarning($"Got maximum number of raycast hits ({RaycastHitsMax}); report to developer");
  1944.         }
  1945.         object target = false;
  1946.         var closest = float.MaxValue;
  1947.         for (var i = 0; i < hitCount; ++i)
  1948.         {
  1949.             var hit = _raycastHits[i];
  1950.             var ent = hit.GetEntity();
  1951.             if (ent is not T || hit.distance >= closest) continue;
  1952.             // prefer car module to chassis unless chassis is significantly closer
  1953.             if (ent is ModularCar && target is BaseVehicleModule && hit.distance >= closest - 2.5f) continue;
  1954.             target = ent;
  1955.             closest = hit.distance;
  1956.         }
  1957.         return target;
  1958.     }
  1959.  
  1960.     private object FindBuilding(Vector3 position, float distance = 3f)
  1961.     {
  1962.         var hit = FindEntity<BuildingBlock>(position, distance);
  1963.         return hit ? hit : FalseObject;
  1964.     }
  1965.  
  1966.     private object FindEntity(Vector3 position, float distance = 3f, params string[] filter)
  1967.     {
  1968.         var hit = FindEntity<BaseEntity>(position, distance, filter);
  1969.         return hit ? hit : FalseObject;
  1970.     }
  1971.  
  1972.     private T FindEntity<T>(Vector3 position, float distance = 3f, params string[] filter) where T : BaseEntity
  1973.     {
  1974.         var list = Pool.Get<List<T>>();
  1975.         Vis.Entities(position, distance, list, _layerMasks);
  1976.  
  1977.         foreach (var e in list)
  1978.         {
  1979.             if (filter.Length <= 0)
  1980.             {
  1981.                 Pool.FreeUnmanaged(ref list);
  1982.                 return e;
  1983.             }
  1984.  
  1985.             foreach (var f in filter)
  1986.             {
  1987.                 if (!e.name.Contains(f)) continue;
  1988.                 Pool.FreeUnmanaged(ref list);
  1989.                 return e;
  1990.             }
  1991.         }
  1992.  
  1993.         Pool.FreeUnmanaged(ref list);
  1994.         return null;
  1995.     }
  1996.  
  1997.     private void FindEntities<T>(List<T> list, Vector3 position, float distance = 3f) where T : BaseEntity =>
  1998.         Vis.Entities(position, distance, list, _layerMasks);
  1999.  
  2000.     // private static void ClearProfile(BasePlayer player)
  2001.     // {
  2002.     //     foreach (var serverEntity in BaseNetworkable.serverEntities)
  2003.     //     {
  2004.     //         if (serverEntity is BaseEntity baseEntity and not BuildingBlock
  2005.     //             && baseEntity.OwnerID == player.userID)
  2006.     //         {
  2007.     //             RemoveOwner(baseEntity);
  2008.     //         }
  2009.     //     }
  2010.     // }
  2011.     //
  2012.     // private void GetProfileConstructions(List<BuildingBlock> result, BasePlayer player)
  2013.     // {
  2014.     //     if (!player) return;
  2015.     //     foreach (var building in BuildingManager.server.buildingDictionary.Values)
  2016.     //     {
  2017.     //         foreach (var block in building.buildingBlocks)
  2018.     //         {
  2019.     //             if (block.OwnerID == player.userID)
  2020.     //             {
  2021.     //                 result.Add(block);
  2022.     //             }
  2023.     //         }
  2024.     //     }
  2025.     // }
  2026.     //
  2027.     // private void GetProfileDeployables(List<BaseEntity> result, BasePlayer player)
  2028.     // {
  2029.     //     if (!player) return;
  2030.     //     foreach (var baseNetworkable in BaseNetworkable.serverEntities)
  2031.     //     {
  2032.     //         if (baseNetworkable is BaseEntity entity and not BuildingBlock &&
  2033.     //             entity.OwnerID == player.userID)
  2034.     //         {
  2035.     //             result.Add(entity);
  2036.     //         }
  2037.     //     }
  2038.     // }
  2039.  
  2040.     private string FindPlayerName(ulong playerID)
  2041.     {
  2042.         if (playerID.IsSteamId())
  2043.         {
  2044.             var player = FindPlayerByPartialName(playerID.ToString());
  2045.             if (player)
  2046.             {
  2047.                 return player.IsSleeping() ?
  2048.                     $"{player.displayName} [<color=#ADD8E6>Sleeping</color>]" :
  2049.                     $"{player.displayName} [<color=#32CD32>Online</color>]";
  2050.             }
  2051.  
  2052.             var p = covalence.Players.FindPlayerById(playerID.ToString());
  2053.             if (p != null)
  2054.             {
  2055.                 return $"{p.Name} [<color=#FF0000>Offline</color>]";
  2056.             }
  2057.         }
  2058.  
  2059.         return $"Unknown: {playerID}";
  2060.     }
  2061.  
  2062.     //only purpose of this function is to provide the same information as GetOwnerName()
  2063.     //but without the color-tag for a debug line added in ChangeOwner()
  2064.     private string GetOwnerDisplayName(BaseEntity entity)
  2065.     {
  2066.         if (!entity) return "<null>";
  2067.  
  2068.         var playerID = entity.OwnerID;
  2069.  
  2070.         if (playerID.IsSteamId())
  2071.         {
  2072.             var player = FindPlayerByPartialName(playerID.ToString());
  2073.             if (player)
  2074.                 return player.displayName;
  2075.  
  2076.             var p = covalence.Players.FindPlayerById(playerID.ToString());
  2077.             if (p != null)
  2078.                 return p.Name;
  2079.         }
  2080.  
  2081.         return $"Unknown: {playerID}";
  2082.     }
  2083.  
  2084.     private ulong FindUserIDByPartialName(string name)
  2085.     {
  2086.         if (string.IsNullOrEmpty(name))
  2087.             return 0;
  2088.  
  2089.         if (ulong.TryParse(name, out var userID))
  2090.         {
  2091.             return userID;
  2092.         }
  2093.  
  2094.         var player = covalence.Players.FindPlayer(name);
  2095.  
  2096.         return player == null ? 0UL : Convert.ToUInt64(player.Id);
  2097.     }
  2098.  
  2099.     private BasePlayer FindPlayerByPartialName(string name) =>
  2100.         string.IsNullOrEmpty(name) ? null : covalence.Players.FindPlayer(name)?.Object as BasePlayer;
  2101.  
  2102.     #endregion
  2103. }
  2104.  
Advertisement
Add Comment
Please, Sign In to add comment