HunterZ

EntityOwner

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