C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//#define DEBUG
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust.Ai.Gen2;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using static Oxide.Plugins.AdminRadarExtensionMethods.ExtensionMethods;
namespace Oxide.Plugins {
[Info("Admin Radar", "nivex", "5.4.0")]
[Description("Radar tool for Admins and Developers.")]
internal class AdminRadar : RustPlugin {
[PluginReference]
Plugin Clans, Backpacks, DiscordMessages;
[Flags]
public enum DrawFlags {
None = 0, Arrow = 1 << 1, Box = 1 << 2, Text = 1 << 3,
}
public enum EntityType {
Active, Airdrop, Bag, Backpack, Boat, Bradley, Car, CargoPlane, CargoShip, CCTV, CH47, Box, Col, TC, TCArrow, Dead, Drop, Limit, Loot, Heli, Mini, MLRS, Npc, Ore, Horse, RHIB, Sleeper, Stash, Trap, Turret
}
private List<string> _tags = new() {
"ore", "cluster", "1", "2", "3", "4", "5", "6", "_", ".", "-", "deployed", "wooden", "large", "pile", "prefab", "collectable", "loot", "small"
}
;
private List<EntityType> _errorTypes = new();
private List<Radar> _radars = new();
private List<BaseEntity> _spawnedEntities = new();
private Dictionary<NetworkableId, Vector3> _despawnedEntities = new();
private Dictionary<NetworkableId, BaseEntity> _allEntities = new();
private Dictionary<string, float> _cooldowns = new();
private Dictionary<ulong, string> _clans = new();
private Dictionary<ulong, string> _teamColors = new();
private Dictionary<string, string> _clanColors = new();
private Array _allEntityTypes = Enum.GetValues(typeof(EntityType));
private CoroutineTimer _coroutineTimer = new(1.0f);
private Stack<Coroutine> _coroutines = new();
private StoredData data = new();
private bool _isPopulatingCache;
private bool isUnloading;
private Cache cache;
private class StoredData {
public Dictionary<ulong, UiOffsets> Offsets = new();
public Dictionary<string, int> EntityTextSize = new();
public Dictionary<string, int> EntityNameSize = new();
public Dictionary<string, int> PlayerTextSize = new();
public Dictionary<string, int> PlayerNameSize = new();
public List<string> Extended = new();
public Dictionary<string, List<string>> Filters = new();
public List<string> Hidden = new();
public List<string> OnlineBoxes = new();
public List<string> Visions = new();
public List<string> Active = new();
public StoredData() {
}
public void Init() {
Offsets ??= new();
EntityTextSize ??= new();
EntityNameSize ??= new();
PlayerTextSize ??= new();
PlayerNameSize ??= new();
Extended ??= new();
Filters ??= new();
Hidden ??= new();
OnlineBoxes ??= new();
Visions ??= new();
Active ??= new();
}
}
private class Cache {
public Cache(AdminRadar instance) {
config = instance.config;
this.instance = instance;
}
public Configuration config;
public AdminRadar instance;
internal EntityType entityType;
public Dictionary<NetworkableId, EntityInfo> Airdrops {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Animals {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Backpacks {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Bags {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Boats {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> BradleyAPCs {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> CargoPlanes {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> CargoShips {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Cars {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> CCTV {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> CH47 {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Cupboards {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Collectibles {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Containers {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Corpses {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Drops {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Helicopters {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> MiniCopter {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> MLRS {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> NPCPlayers {
get;
set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Ores {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> RHIB {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> RidableHorse {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Turrets {
get;
private set;
}
= new();
public Dictionary<NetworkableId, EntityInfo> Traps {
get;
private set;
}
= new();
public bool Add(BaseEntity entity) {
if (entity.IsKilled()) {
return false;
}
if (IsDrop(entity)) {
Add_Internal<BaseEntity, EntityInfo>(Drops, entity, EntityType.Drop);
}
if (entity is BasePlayer player) {
Interface.Oxide.NextTick(() => {
if (player.IsKilled()) {
return;
}
if (player.userID.IsSteamId()) {
instance._radars.ForEach(radar => radar.TryCacheOnlinePlayer(player));
}
else if (config.Core.NPCPlayer && !NPCPlayers.TryGetValue(entity.net.ID, out var ei)) {
NPCPlayers[entity.net.ID] = ei = new(player, EntityType.Npc, config.Distance.Get);
instance.TryCacheByType(EntityType.Npc, ei);
}
}
);
return false;
}
if (config.Core.Dead && entity is PlayerCorpse corpse) {
Interface.Oxide.NextTick(() => {
if (!corpse.IsKilled() && corpse.playerSteamID.IsSteamId() && !Corpses.TryGetValue(entity.net.ID, out var ei)) {
Corpses[entity.net.ID] = ei = new(entity, EntityType.Dead, config.Distance.Get);
ei.name = corpse.parentEnt?.ToString() ?? corpse.playerSteamID.ToString();
instance.TryCacheByType(EntityType.Dead, ei);
}
}
);
return false;
}
if ((config.Additional.Traps || config.GUI.Traps) && IsTrap(entity) && Add_Internal<BaseEntity, EntityInfo>(Traps, entity, EntityType.Trap)) {
return true;
}
if (config.Additional.RH && Add_Internal<RidableHorse, EntityInfo>(RidableHorse, entity, EntityType.Horse)) {
return true;
}
if (config.Core.Animals && Add_Internal<BaseNpc, EntityInfo>(Animals, entity, EntityType.Npc)) {
return true;
}
if (config.Core.Animals && Add_Internal<BaseNPC2, EntityInfo>(Animals, entity, EntityType.Npc)) {
return true;
}
if (config.Core.Animals && Add_Internal<SimpleShark, EntityInfo>(Animals, entity, EntityType.Npc)) {
return true;
}
if (config.Core.Loot && entity is TrainCarUnloadable tcu && Add_Internal<StorageContainer, EntityInfo>(Containers, tcu.GetStorageContainer(), EntityType.Loot)) {
return true;
}
if ((config.Core.Loot || config.Core.Box || config.Core.Stash) && entity is StorageContainer && TryGetContainerType(entity, out entityType) && Add_Internal<StorageContainer, EntityInfo>(Containers, entity, entityType)) {
return true;
}
if (config.Core.Col && entity is CollectibleEntity && Add_Internal<CollectibleEntity, EntityInfo>(Collectibles, entity, EntityType.Col)) {
return true;
}
if (config.Core.Ore && entity is OreResourceEntity && Add_Internal<OreResourceEntity, EntityInfo>(Ores, entity, EntityType.Ore)) {
return true;
}
if ((config.Additional.Cars || config.GUI.Cars) && (Add_Internal<BasicCar, EntityInfo>(Cars, entity, EntityType.Car) || Add_Internal<ModularCar, EntityInfo>(Cars, entity, EntityType.Car) || Add_Internal<Bike, EntityInfo>(Cars, entity, EntityType.Car))) {
return true;
}
if ((config.Additional.CP || config.GUI.CP) && entity.prefabID == 2383782438 && Add_Internal<BaseEntity, EntityInfo>(CargoPlanes, entity, EntityType.CargoPlane)) {
return true;
}
if (config.Core.Bags && Add_Internal<SleepingBag, EntityInfo>(Bags, entity, EntityType.Bag)) {
return true;
}
if (config.Core.TC && Add_Internal<BuildingPrivlidge, EntityInfo>(Cupboards, entity, EntityType.TC)) {
return true;
}
if ((config.Additional.CCTV || config.GUI.CCTV) && Add_Internal<CCTV_RC, EntityInfo>(CCTV, entity, EntityType.CCTV)) {
return true;
}
if (config.Core.Airdrop && Add_Internal<SupplyDrop, EntityInfo>(Airdrops, entity, EntityType.Airdrop)) {
return true;
}
if (config.Core.Loot && Add_Internal<DroppedItemContainer, EntityInfo>(Backpacks, entity, EntityType.Backpack)) {
return true;
}
if (config.Core.Loot && entity is WorldItem wi && wi.item != null && wi.item.IsBackpack() && Add_Internal<WorldItem, EntityInfo>(Backpacks, entity, EntityType.Backpack)) {
return true;
}
if ((config.Additional.Heli || config.GUI.Heli) && Add_Internal<PatrolHelicopter, EntityInfo>(Helicopters, entity, EntityType.Heli)) {
return true;
}
if ((config.Additional.Bradley || config.GUI.Bradley) && (Add_Internal<BradleyAPC, EntityInfo>(BradleyAPCs, entity, EntityType.Bradley))) // || Add_Internal<TravellingVendor, EntityInfo>(BradleyAPCs, entity, EntityType.Bradley)))
{
return true;
}
if ((config.Additional.RHIB || config.GUI.RHIB) && Add_Internal<RHIB, EntityInfo>(RHIB, entity, EntityType.RHIB)) {
return true;
}
if ((config.Additional.Boats || config.GUI.Boats) && Add_Internal<BaseBoat, EntityInfo>(Boats, entity, EntityType.Boat)) {
return true;
}
if ((config.Additional.MC || config.GUI.MC) && (Add_Internal<Minicopter, EntityInfo>(MiniCopter, entity, EntityType.Mini) || Add_Internal<AttackHelicopter, EntityInfo>(MiniCopter, entity, EntityType.Mini))) {
return true;
}
if ((config.Additional.CH47 || config.GUI.CH47) && Add_Internal<CH47Helicopter, EntityInfo>(CH47, entity, EntityType.CH47)) {
return true;
}
if ((config.Additional.CS || config.GUI.CS) && Add_Internal<CargoShip, EntityInfo>(CargoShips, entity, EntityType.CargoShip)) {
return true;
}
if (config.Core.Turrets && Add_Internal<AutoTurret, EntityInfo>(Turrets, entity, EntityType.Turret)) {
return true;
}
if ((config.Additional.MLRS || config.GUI.MLRS) && Add_Internal<MLRSRocket, EntityInfo>(MLRS, entity, EntityType.MLRS)) {
return true;
}
return false;
}
private bool TryGetContainerType(BaseEntity entity, out EntityType type) {
if (entity is LockedByEntCrate || IsLoot(entity)) {
type = EntityType.Loot;
return true;
}
if (entity is StashContainer) {
type = EntityType.Stash;
return true;
}
if (IsBox(entity)) {
type = EntityType.Box;
return true;
}
type = (EntityType)0;
return false;
}
public bool Remove(NetworkableId nid, Vector3 entityPos) {
instance._radars.ForEach(radar => radar.RemoveByNetworkId(nid));
if (Remove_Internal(Airdrops, nid)) return true;
if (Remove_Internal(Animals, nid)) return true;
if (Remove_Internal(Backpacks, nid)) return true;
if (Remove_Internal(Bags, nid)) return true;
if (Remove_Internal(Boats, nid)) return true;
if (Remove_Internal(BradleyAPCs, nid)) return true;
if (Remove_Internal(CargoPlanes, nid)) return true;
if (Remove_Internal(CargoShips, nid)) return true;
if (Remove_Internal(Cars, nid)) return true;
if (Remove_Internal(CCTV, nid)) return true;
if (Remove_Internal(CH47, nid)) return true;
if (Remove_Internal(Collectibles, nid)) return true;
if (Remove_Internal(Containers, nid)) return true;
if (Remove_Internal(Corpses, nid)) return true;
if (Remove_Internal(Cupboards, nid)) return true;
if (Remove_Internal(Helicopters, nid)) return true;
if (Remove_Internal(MiniCopter, nid)) return true;
if (Remove_Internal(MLRS, nid)) return true;
if (Remove_Internal(NPCPlayers, nid)) return true;
if (Remove_Internal(Ores, nid)) return true;
if (Remove_Internal(RHIB, nid)) return true;
if (Remove_Internal(RidableHorse, nid)) return true;
if (Remove_Internal(Traps, nid)) return true;
if (Remove_Internal(Turrets, nid)) return true;
return false;
}
private bool Add_Internal<TLookFor, TTargetType>(Dictionary<NetworkableId, EntityInfo> cachedList, BaseEntity entity, EntityType type) {
if (entity is TLookFor && !entity.IsKilled() && !cachedList.TryGetValue(entity.net.ID, out var ei)) {
cachedList[entity.net.ID] = ei = new(entity, type, config.Distance.Get, instance.StripTags);
instance.TryCacheByType(type, ei);
return true;
}
return false;
}
private bool Remove_Internal<TType, TKeyType>(Dictionary<TKeyType, TType> cachedList, TKeyType key) {
return cachedList.Remove(key);
}
#if DEBUG
public void Print() {
StringBuilder sb = StringBuilderCache.Acquire();
sb.AppendLine($"{nameof(Airdrops)}: {Airdrops.Count}");
sb.AppendLine($"{nameof(Animals)}: {Animals.Count}");
sb.AppendLine($"{nameof(Backpacks)}: {Backpacks.Count}");
sb.AppendLine($"{nameof(Bags)}: {Bags.Count}");
sb.AppendLine($"{nameof(Boats)}: {Boats.Count}");
sb.AppendLine($"{nameof(BradleyAPCs)}: {BradleyAPCs.Count}");
sb.AppendLine($"{nameof(CargoPlanes)}: {CargoPlanes.Count}");
sb.AppendLine($"{nameof(CargoShips)}: {CargoShips.Count}");
sb.AppendLine($"{nameof(Cars)}: {Cars.Count}");
sb.AppendLine($"{nameof(CCTV)}: {CCTV.Count}");
sb.AppendLine($"{nameof(CH47)}: {CH47.Count}");
sb.AppendLine($"{nameof(Collectibles)}: {Collectibles.Count}");
sb.AppendLine($"{nameof(Containers)}: {Containers.Count}");
sb.AppendLine($"{nameof(Corpses)}: {Corpses.Count}");
sb.AppendLine($"{nameof(Cupboards)}: {Cupboards.Count}");
sb.AppendLine($"{nameof(Helicopters)}: {Helicopters.Count}");
sb.AppendLine($"{nameof(MiniCopter)}: {MiniCopter.Count}");
sb.AppendLine($"{nameof(MLRS)}: {MLRS.Count}");
sb.AppendLine($"{nameof(NPCPlayers)}: {NPCPlayers.Count}");
sb.AppendLine($"{nameof(Ores)}: {Ores.Count}");
sb.AppendLine($"{nameof(RHIB)}: {RHIB.Count}");
sb.AppendLine($"{nameof(RidableHorse)}: {RidableHorse.Count}");
sb.AppendLine($"{nameof(Traps)}: {Traps.Count}");
sb.AppendLine($"{nameof(Turrets)}: {Turrets.Count}");
instance.Puts(StringBuilderCache.GetStringAndRelease(sb));
}
#endif
public bool IsDrop(BaseEntity entity) {
if (entity is DroppedItem || entity is Landmine || entity is BearTrap || entity is DroppedItemContainer || entity is RFTimedExplosive) {
return !config.Settings.DropExceptions.Exists(entity.ShortPrefabName.Contains);
}
return false;
}
public bool IsTrap(BaseNetworkable entity) {
return entity is BaseTrap || config.Options.AdditionalTraps.Exists(entity.ShortPrefabName.Contains);
}
public bool IsLoot(BaseNetworkable entity) {
if (config.Core.Loot) {
if (entity is WorldItem wi) {
return wi.item != null && wi.item.IsBackpack();
}
return entity is TrainCarUnloadable || entity.ShortPrefabName == "campfire" || entity.ShortPrefabName.Contains("loot", CompareOptions.IgnoreCase) || entity.ShortPrefabName.Contains("crate_", CompareOptions.IgnoreCase) || entity.ShortPrefabName.Contains("trash", CompareOptions.IgnoreCase) || entity.ShortPrefabName.Contains("hackable", CompareOptions.IgnoreCase) || entity.ShortPrefabName.Contains("oil", CompareOptions.IgnoreCase) || entity.ShortPrefabName.Contains("vehicle_parts") || entity.ShortPrefabName.Contains("foodbox");
}
return false;
}
public bool IsBox(BaseNetworkable entity) {
if (config.Core.Box) {
return config.Options.AdditionalBoxes.Exists(entity.ShortPrefabName.Contains);
}
return false;
}
}
private class CoroutineTimer {
private Stopwatch stopwatch = new();
private float _maxDurationMs;
private bool _isRunning;
//public bool ForceStop { get; set; }
public double Elapsed => stopwatch.Elapsed.TotalMilliseconds;
public CoroutineTimer(float maxDurationMs) {
_maxDurationMs = maxDurationMs;
}
public void Start() {
stopwatch.Start();
_isRunning = true;
}
public bool ShouldYield() {
_isRunning = stopwatch.Elapsed.TotalMilliseconds < _maxDurationMs;
if (!_isRunning) {
#if DEBUG
double milliseconds = Elapsed;
if (stopwatch.Elapsed.TotalMilliseconds > 2f * _maxDurationMs) {
Interface.Oxide.LogInfo($"[AdminRadar] Time slice took {Elapsed} when it should be around {_maxDurationMs}");
}
#endif
}
return !_isRunning;
}
public void ResetIfYielded() {
if (_isRunning) return;
stopwatch.Restart();
_isRunning = true;
}
}
internal static class StringBuilderCache {
[ThreadStatic]
private static StringBuilder builder;
public static StringBuilder Acquire(string text = "") {
StringBuilder cached = builder;
if (cached != null) {
builder = null;
cached.Clear();
cached.Append(text);
return cached;
}
return new(text);
}
public static void Clear() {
if (builder != null) {
builder.Clear();
builder = null;
}
}
public static string GetStringAndRelease(StringBuilder sb) {
string result = sb.ToString();
builder = sb;
return result;
}
}
public class EntityInfo {
public BuildingPrivlidge priv;
public BaseEntity entity;
public EntityType type;
public Color color;
public Vector3 _from;
public Vector3 to;
public Vector3 offset;
public string name;
public object info;
public float dist;
public float sqrdist;
public float size = 0.5f;
public Transform t;
public Network.Visibility.Group group => entity?.net?.group;
public Vector3 from => (t == null ? _from : _from = t.position) + offset;
public EntityInfo() {
}
public EntityInfo(BaseEntity entity, EntityType type, Func<EntityType, BaseEntity, float> getDistance, Func<string, string> stripTags = null) {
if (stripTags != null) {
name = stripTags(entity.ShortPrefabName);
}
if (getDistance != null) {
dist = getDistance(type, entity);
sqrdist = dist * dist;
}
if (type == EntityType.TC) {
priv = entity as BuildingPrivlidge;
}
this.type = type;
this.entity = entity;
t = entity.transform;
_from = t.position;
}
}
private class Radar : FacepunchBehaviour {
private static Func<char, bool> abbr = c => char.IsUpper(c) || char.IsDigit(c);
public class DataObject : Pool.IPooled {
public EntityInfo ei;
public Action action;
public DrawFlags flags;
public bool disabled;
public DataObject() {
}
public bool HasFlag(DrawFlags flag) {
return ((flags & flag) == flag);
}
public void SetEnabled(Network.Visibility.Group group, Vector3 to, float max) {
disabled = ei == null || ei.group != group && (ei.from - to).sqrMagnitude > Mathf.Max(max * max, ei.sqrdist);
}
public bool IsOfType(EntityType type) {
if (type != EntityType.Loot) {
return ei.type == type;
}
return ei.type == EntityType.Loot || ei.type == EntityType.Backpack;
}
public void Reset() {
ei = null;
action = null;
disabled = false;
flags = DrawFlags.None;
}
public void EnterPool() {
Reset();
}
public void LeavePool() {
Reset();
}
}
internal class DistantPlayer : Pool.IPooled {
public Vector3 pos;
public bool alive;
public DistantPlayer() {
}
public void Reset() {
pos = default;
alive = false;
}
public void EnterPool() {
Reset();
}
public void LeavePool() {
Reset();
}
}
internal bool setSource = true, canGetExistingBackpacks = true, isEnabled = true, canBypassOverride, hasPermAllowed, isAdmin, showHT, showAll;
internal int inactiveSeconds, activatedSeconds, checks;
internal float currDistance, invokeTime, maxDistance;
internal string username, userid;
internal RaycastHit hit;
internal EntityType currType;
internal Vector3 position;
internal BaseEntity source;
internal BasePlayer player;
internal AdminRadar instance;
internal Network.Visibility.Group group;
internal ItemContainer _backpackItemContainer;
internal Coroutine _radarCo, _updateCo, _groupCo;
internal List<ulong> exclude = Pool.Get<List<ulong>>();
internal List<NetworkableId> removeByNetworkId = Pool.Get<List<NetworkableId>>();
internal List<EntityType> entityTypes = Pool.Get<List<EntityType>>();
internal List<DistantPlayer> distant = Pool.Get<List<DistantPlayer>>();
internal List<EntityType> removeByEntityType = Pool.Get<List<EntityType>>();
internal Dictionary<NetworkableId, DataObject> data = Pool.Get<Dictionary<NetworkableId, DataObject>>();
internal Dictionary<EntityType, Action> filters = Pool.Get<Dictionary<EntityType, Action>>();
internal Dictionary<ulong, ItemContainer> backpacks = Pool.Get<Dictionary<ulong, ItemContainer>>();
internal Dictionary<ulong, float> Voices = Pool.Get<Dictionary<ulong, float>>();
internal List<ulong> VoiceExpirations = Pool.Get<List<ulong>>();
internal Cache Cache => instance.cache;
internal Configuration config => instance.config;
internal float delay => invokeTime + 0.05f;
internal float Distance(Vector3 a) {
float sqrMagnitude = (a - position).sqrMagnitude;
return Mathf.Ceil(Mathf.Sqrt(sqrMagnitude));
}
public Vector3 limitUp;
public Vector3 halfUp = new(0f, 0.5f);
public Vector3 twoHalfUp = new(0f, 2.5f);
public Vector3 twoUp = new(0f, 2f);
public Vector3 fiveUp = new(0f, 5f);
private void Awake() {
source = player = GetComponent<BasePlayer>();
isAdmin = player.IsAdmin;
userid = player.UserIDString;
username = player.displayName;
position = player.transform.position;
exclude.Add(player.userID);
}
private void OnDestroy() {
isEnabled = false;
Interface.CallHook("OnRadarDeactivated", player, username, userid, position);
if (config.Settings.Cooldown > 0f) {
instance._cooldowns[userid] = Time.realtimeSinceStartup + config.Settings.Cooldown;
}
if (config.Settings.ShowToggle) {
instance.Message(player, "Deactivated");
}
if (instance._radars.Remove(this) && instance._radars.Count == 0 && !instance.isUnloading) {
instance.Unsubscribe(nameof(OnPlayerRespawned));
}
instance.DestroyUI(player);
ResetToPool();
StopAll();
}
private void ResetToPool() {
for (int i = distant.Count - 1; i >= 0; i--) {
var obj = distant[i];
if (obj != null) {
Pool.Free(ref obj);
}
distant[i] = null;
}
for (int i = data.Count - 1; i >= 0; i--) {
var obj = data.ElementAt(i);
if (obj.Value != null) {
var value = obj.Value;
value.Reset();
Pool.Free(ref value);
}
}
data.ResetToPool();
Voices.ResetToPool();
exclude.ResetToPool();
distant.ResetToPool();
filters.ResetToPool();
backpacks.ResetToPool();
entityTypes.ResetToPool();
VoiceExpirations.ResetToPool();
removeByNetworkId.ResetToPool();
removeByEntityType.ResetToPool();
}
public bool Add(EntityType type) {
if (type == EntityType.Loot && !entityTypes.Contains(EntityType.Backpack)) {
entityTypes.Add(EntityType.Backpack);
}
if (!entityTypes.Contains(type)) {
entityTypes.Add(type);
return true;
}
return false;
}
public void Init(AdminRadar instance) {
this.instance = instance;
this.limitUp = new(0f, config.Limit.Height);
this.instance._radars.Add(this);
canBypassOverride = HasPermission(userid, "adminradar.bypass.override");
hasPermAllowed = HasPermission(userid, "adminradar.allowed");
InvokeRepeating(Activity, 0f, 1f);
Interface.CallHook("OnRadarActivated", player, username, userid, position);
if (instance._radars.Count == 1) {
instance.Subscribe(nameof(OnPlayerRespawned));
}
}
public void StopAll() {
if (_radarCo != null) {
StopCoroutine(_radarCo);
_radarCo = null;
}
if (_updateCo != null) {
StopCoroutine(_updateCo);
_updateCo = null;
}
if (_groupCo != null) {
StopCoroutine(_groupCo);
_groupCo = null;
}
try {
CancelInvoke(Activity);
}
catch {
}
try {
RemoveAdminFlag();
}
catch {
}
}
public bool GetBool(EntityType type) {
return entityTypes.Contains(type);
}
private void Activity() {
inactiveSeconds = position == player.transform.position ? inactiveSeconds + 1 : 0;
position = source.transform.position;
if (source != player) {
inactiveSeconds = 0;
return;
}
if (config.Settings.DeactivateSeconds > 0 && ++activatedSeconds >= config.Settings.DeactivateSeconds) {
isEnabled = false;
Destroy(this);
}
if (config.Settings.InactiveSeconds > 0 && inactiveSeconds >= config.Settings.InactiveSeconds) {
isEnabled = false;
Destroy(this);
}
}
private class Idle {
public BasePlayer player;
public Vector3 lastPosition;
public float lastMovementTime;
}
private Dictionary<ulong, Idle> _idle = new();
private double GetIdleTime(BasePlayer target) {
Vector3 currPosition = target.transform.position;
if (!_idle.TryGetValue(target.userID, out var idle)) {
_idle[target.userID] = idle = new();
idle.lastPosition = currPosition;
idle.lastMovementTime = Time.time;
idle.player = target;
return 0;
}
if (idle.lastPosition != currPosition) {
idle.lastPosition = currPosition;
idle.lastMovementTime = Time.time;
}
double time = Math.Round((Time.time - idle.lastMovementTime) / 60.0, config.Settings.IdleRoundDigits);
return time > 0 ? time : 0;
}
private void RemoveIdleTime() {
if (_idle.Count > 0) {
foreach (var pair in _idle) {
if (pair.Value.player == null || !pair.Value.player.IsConnected) {
_idle.Remove(pair.Key);
break;
}
}
}
}
private void SetupFilter(EntityType type, Action action) {
if (entityTypes.Contains(type)) {
if (_radarCo != null) {
action();
}
filters[type] = action;
}
else if (filters.Remove(type)) {
RemoveByEntityType(type);
}
}
public void SetupFilters(bool barebones) {
if (_updateCo != null) {
StopCoroutine(_updateCo);
}
SetupFilter(EntityType.Active, ShowActive);
SetupFilter(EntityType.Sleeper, ShowSleepers);
if (!barebones) {
SetupFilters();
}
if (_radarCo == null) {
CheckNetworkGroupChange();
_radarCo = StartCoroutine(DoRadarRoutine());
}
if (_groupCo == null && config.Limit.Enabled) {
_groupCo = StartCoroutine(DoGroupLimitRoutine());
}
DoRemoves();
}
private void SetupFilters() {
SetupFilter(EntityType.TC, ShowTC);
SetupFilter(EntityType.Bag, ShowBags);
SetupFilter(EntityType.Box, ShowBox);
SetupFilter(EntityType.Ore, ShowOre);
SetupFilter(EntityType.Npc, ShowNPC);
SetupFilter(EntityType.CCTV, ShowCCTV);
SetupFilter(EntityType.Dead, ShowDead);
SetupFilter(EntityType.Drop, ShowDrops);
SetupFilter(EntityType.Heli, ShowHeli);
SetupFilter(EntityType.Loot, ShowLoot);
SetupFilter(EntityType.Stash, ShowStash);
SetupFilter(EntityType.Turret, ShowTurrets);
SetupFilter(EntityType.Bradley, ShowBradley);
SetupFilter(EntityType.Col, ShowCollectibles);
SetupFilter(EntityType.Airdrop, ShowSupplyDrops);
SetupFilter(EntityType.Car, () => ShowEntity(EntityType.Car, Cache.Cars));
SetupFilter(EntityType.CH47, () => ShowEntity(EntityType.CH47, Cache.CH47));
SetupFilter(EntityType.MLRS, () => ShowEntity(EntityType.MLRS, Cache.MLRS));
SetupFilter(EntityType.RHIB, () => ShowEntity(EntityType.RHIB, Cache.RHIB));
SetupFilter(EntityType.Boat, () => ShowEntity(EntityType.Boat, Cache.Boats));
SetupFilter(EntityType.Trap, () => ShowEntity(EntityType.Trap, Cache.Traps));
SetupFilter(EntityType.Mini, () => ShowEntity(EntityType.Mini, Cache.MiniCopter));
SetupFilter(EntityType.Horse, () => ShowEntity(EntityType.Horse, Cache.RidableHorse));
SetupFilter(EntityType.CargoShip, () => ShowEntity(EntityType.CargoShip, Cache.CargoShips));
SetupFilter(EntityType.CargoPlane, () => ShowEntity(EntityType.CargoPlane, Cache.CargoPlanes));
}
private IEnumerator DoGroupLimitRoutine() {
while (isEnabled) {
ShowGroupLimits();
yield return CoroutineEx.waitForSeconds(invokeTime);
}
}
private IEnumerator DoUpdateRoutine() {
SetEnabledDataObjects();
foreach (var filter in filters) {
filter.Value();
if (checks >= 100) {
checks = 0;
yield return CoroutineEx.waitForFixedUpdate;
}
}
}
private IEnumerator DoRadarRoutine() {
while (isEnabled) {
if (player == null || !player.IsConnected || instance.isUnloading) {
isEnabled = false;
Destroy(this);
yield break;
}
if (!SetSource()) {
yield return CoroutineEx.waitForSeconds(0.1f);
continue;
}
DoRemoves();
SetAdminFlag();
DirectDrawAll();
RemoveAdminFlag();
CheckNetworkGroupChange();
checks = 0;
yield return CoroutineEx.waitForSeconds(invokeTime);
}
}
private void DoRemoves() {
if (data.Count != 0) {
foreach (var pair in data) {
if (pair.Value.ei == null || !pair.Value.ei.entity.IsValid()) {
removeByNetworkId.Add(pair.Key);
pair.Value.disabled = true;
}
}
}
if (removeByEntityType.Count != 0) {
foreach (var type in removeByEntityType) {
RemoveByEntityType(type);
entityTypes.Remove(type);
filters.Remove(type);
}
removeByEntityType.Clear();
}
if (removeByNetworkId.Count != 0) {
foreach (var nid in removeByNetworkId) {
RemoveByNetworkId(nid);
}
removeByNetworkId.Clear();
}
}
private void CheckNetworkGroupChange() {
if (group == player.net.group) {
return;
}
group = player.net.group;
if (_updateCo != null) {
StopCoroutine(_updateCo);
}
if (filters.ContainsKey(EntityType.Npc)) {
RemoveByEntityType(EntityType.Npc);
DoRemoves();
}
_updateCo = StartCoroutine(DoUpdateRoutine());
}
public void SetEnabledDataObjects() {
foreach (var pair in data) {
pair.Value.SetEnabled(group, position, maxDistance);
}
}
public void RemoveByEntityType(EntityType type) {
foreach (var pair in data) {
if (pair.Value.IsOfType(type)) {
removeByNetworkId.Add(pair.Key);
pair.Value.disabled = true;
}
}
}
public void RemoveByNetworkId(NetworkableId nid) {
if (data.Remove(nid, out var obj)) {
if (obj != null) {
Pool.Free(ref obj);
}
}
}
public void TryCacheByType(EntityType type, EntityInfo ei) {
try {
if (!entityTypes.Contains(type) || !ei.entity.IsValid() || ei.entity.net.group != group && Distance(ei._from) > ei.dist) {
return;
}
switch (type) {
case EntityType.Airdrop : {
CacheAirdrop(ei);
break;
}
case EntityType.Backpack : {
CacheBackpack(ei);
break;
}
case EntityType.Bag : {
CacheSleepingBag(ei);
break;
}
case EntityType.Box : {
CacheContainer(ei);
break;
}
case EntityType.Bradley : {
CacheBradley(ei);
break;
}
case EntityType.CCTV : {
CacheCCTV(ei);
break;
}
case EntityType.Col : {
CacheCol(ei);
break;
}
case EntityType.Dead : {
CacheDead(ei);
break;
}
case EntityType.Heli : {
CacheHeli(ei);
break;
}
case EntityType.Loot : {
CacheLoot(ei);
break;
}
case EntityType.Npc : {
CacheNpc(ei);
break;
}
case EntityType.Ore : {
CacheOre(ei);
break;
}
case EntityType.Stash : {
CacheStash(ei);
break;
}
case EntityType.TC : {
CacheTC(ei);
break;
}
case EntityType.Turret : {
CacheTurret(ei);
break;
}
default : {
CacheEntity(ei, type);
break;
}
}
}
catch (Exception ex) {
currType = type;
HandleException(ex);
}
}
private DataObject cobj;
private DrawFlags cflag;
private void DirectDrawAll() {
float delay = this.delay;
foreach (var pair in data) {
cobj = pair.Value;
if (cobj == null || cobj.ei == null || cobj.disabled) {
continue;
}
cflag = DrawFlags.Arrow;
if (cobj.HasFlag(DrawFlags.Arrow)) {
player.SendConsoleCommand("ddraw.arrow", delay, cobj.ei.color, cobj.ei.from, cobj.ei.to, cobj.ei.size);
}
cflag = DrawFlags.Box;
if (cobj.HasFlag(DrawFlags.Box)) {
player.SendConsoleCommand("ddraw.box", delay, cobj.ei.color, cobj.ei.from, cobj.ei.size);
}
cflag = DrawFlags.Text;
if (cobj.HasFlag(DrawFlags.Text)) {
try {
cobj.action();
}
catch (Exception ex) {
HandleException(pair.Key, ex);
continue;
}
if (cobj.ei.info == null) {
continue;
}
player.SendConsoleCommand("ddraw.text", delay, cobj.ei.color, cobj.ei.from, cobj.ei.info);
}
}
}
private void DrawVisionArrow(BasePlayer target, float dist) {
if (dist <= 150f && instance.data.Visions.Contain
// ... [Code preview truncated due to length. Full source in attached file] ...