Иконка ресурса

FREE RaidTracker : 2.1.1 2.1.1

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Oxide.Plugins {
    [Info("Raid Tracker", "Clearshot", "2.1.1")]
    [Description("Track raids by explosives, weapons, and ammo with detailed on-screen visuals")]
    class RaidTracker : CovalencePlugin {
        private bool _dev = false;
        // force certain config options
        private bool _debug = false;
        // respect config, detailed output
        private float _debugDrawDuration = 15f;
        private static RaidTracker _instance;
        private PluginConfig _config;
        private bool _isConfigValid = true;
        private StringBuilder _sb = new StringBuilder();
        private Game.Rust.Libraries.Player _rustPlayer = Interface.Oxide.GetLibrary<Game.Rust.Libraries.Player>("Player");
        private DiscordWebhookManager _discordWebhookManager = new DiscordWebhookManager();
        private readonly int _collisionLayerMask = LayerMask.GetMask("Construction", "Deployed");
        private Dictionary<Vector3, ulong> _MLRSRocketOwners = new Dictionary<Vector3, ulong>();
        private Dictionary<ulong, bool> _verboseMode = new Dictionary<ulong, bool>();
        private Dictionary<ulong, string[]> _lastViewCommand = new Dictionary<ulong, string[]>();
        private Dictionary<string, DecayEntityIgnoreOptions> _decayEntityIgnoreList = new Dictionary<string, DecayEntityIgnoreOptions>();
        private Dictionary<string, string> _prefabToItem = new Dictionary<string, string>();
        private Dictionary<string, string> _buildingBlockPrettyNames = new Dictionary<string, string>();
        private Dictionary<ulong, float> _notificationCooldown = new Dictionary<ulong, float>();
        private bool _wipeData;
        private List<RaidEvent> _raidEventLog = new List<RaidEvent>();
        private string _raidEventLogFilename;
        private int _raidEventLogCount;
        private string[] _ignoredTimedExplosives = new string[] {
            "firecrackers.deployed", "flare.deployed", "maincannonshell", "rocket_heli", "rocket_heli_napalm"
        }
        ;
        private string[] _uniqueHexColors = new string[] {
            "#01FFFE", "#FFA6FE", "#FFDB66", "#006401", "#010067", "#95003A", "#007DB5", "#FF00F6", "#FFEEE8", "#774D00", "#90FB92", "#0076FF", "#D5FF00", "#FF937E", "#6A826C", "#FF029D", "#FE8900", "#7A4782", "#7E2DD2", "#85A900", "#FF0056", "#A42400", "#00AE7E", "#683D3B", "#BDC6FF", "#263400", "#BDD393", "#00B917", "#9E008E", "#001544", "#C28C9F", "#FF74A3", "#01D0FF", "#004754", "#E56FFE", "#788231", "#0E4CA1", "#91D0CB", "#BE9970", "#968AE8", "#BB8800", "#43002C", "#DEFF74", "#00FFC6", "#FFE502", "#620E00", "#008F9C", "#98FF52", "#7544B1", "#B500FF", "#00FF78", "#FF6E41", "#005F39", "#6B6882", "#5FAD4E", "#A75740", "#A5FFD2", "#FFB167", "#009BFF", "#E85EBE", "#00FF00", "#0000FF", "#FF0000", "#000000"
        }
        ;
        private List<Color> _uniqueColors = new List<Color>();
        private Dictionary<ulong, Color> _teamColors = new Dictionary<ulong, Color>();
        private int _currentTeamColorIdx = 0;
        [PluginReference]
        Plugin AbandonedBases, Clans, RaidableBases;
        private const string PERM_WIPE = "raidtracker.wipe";
        private const string PERM_PX = "raidtracker.px";
#region Hooks
        private void Init() {
            permission.RegisterPermission(PERM_WIPE, this);
            permission.RegisterPermission(PERM_PX, this);
            Unsubscribe(nameof(OnPlayerDisconnected));
            Unsubscribe(nameof(OnMlrsFired));
            Unsubscribe(nameof(OnMlrsFiringEnded));
            Unsubscribe(nameof(OnEntitySpawned));
            Unsubscribe(nameof(OnEntityDeath));
            Unsubscribe(nameof(OnServerSave));
        }
        private void OnServerInitialized() {
            _debug = _debug || _config.debug;
            if (_dev) {
                _debug = true;
                Puts("[DEV] Dev mode enabled!");
            }
            PrintDebug("Debug mode enabled!");
            _raidEventLogFilename = $"{Name}\\RaidEventLog";
            _raidEventLog = Interface.Oxide.DataFileSystem.ReadObject<List<RaidEvent>>(_raidEventLogFilename);
            _raidEventLogCount = _raidEventLog.Count;
            foreach (string hexColor in _uniqueHexColors) {
                Color color;
                if (ColorUtility.TryParseHtmlString(hexColor, out color)) _uniqueColors.Add(color);
            }
            foreach (var item in ItemManager.itemList.OrderBy(x => x.shortname)) {
                ItemModEntity itemModEnt = item.GetComponent<ItemModEntity>();
                if (itemModEnt != null) {
                    var gameObjRef = itemModEnt.entityPrefab;
                    if (string.IsNullOrEmpty(gameObjRef.guid)) continue;
                    AddPrefabToItem(gameObjRef.resourcePath, item.shortname, nameof(ItemModEntity));
                }
                ItemModDeployable itemModDeploy = item.GetComponent<ItemModDeployable>();
                if (itemModDeploy != null) {
                    var gameObjRef = itemModDeploy.entityPrefab;
                    if (string.IsNullOrEmpty(gameObjRef.guid)) continue;
                    AddPrefabToItem(gameObjRef.resourcePath, item.shortname, nameof(ItemModDeployable));
                }
                ItemModProjectile itemModProj = item.GetComponent<ItemModProjectile>();
                if (itemModProj != null) {
                    var gameObjRef = itemModProj.projectileObject;
                    if (string.IsNullOrEmpty(gameObjRef.guid)) continue;
                    AddPrefabToItem(gameObjRef.resourcePath, item.shortname, nameof(ItemModProjectile));
                }
            }
            if (_wipeData && _config.deleteDataOnWipe) {
                Puts($"Wipe detected! Removing {_raidEventLog.Count} raid events.");
                _wipeData = false;
                _raidEventLog = new List<RaidEvent>();
                SaveRaidEventLog();
            }
            if (_raidEventLog.Count > 0) {
                int removed = 0;
                DateTime currentDateTime = DateTime.Now;
                for (int i = _raidEventLog.Count - 1; i >= 0; i--) {
                    RaidEvent raidEvent = _raidEventLog[i];
                    if (currentDateTime.Subtract(raidEvent.timestamp).TotalDays >= _config.daysBeforeDelete) {
                        _raidEventLog.RemoveAt(i);
                        removed++;
                    }
                }
                if (removed > 0) {
                    Puts($"Removed {removed} raid events older than {_config.daysBeforeDelete} days");
                    SaveRaidEventLog();
                }
            }
            var saveList = false;
            var decayEntityListFilename = $"{Name}\\DecayEntityIgnoreList";
            if (!Interface.Oxide.DataFileSystem.ExistsDatafile(decayEntityListFilename)) {
                Puts($"Generating DecayEntityIgnoreList, any items enabled in this list will be ignored by the plugin");
                Puts($"Saving DecayEntityIgnoreList to /oxide/data/{Utility.CleanPath(decayEntityListFilename)}.json");
            }
            _decayEntityIgnoreList = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, DecayEntityIgnoreOptions>>(decayEntityListFilename);
            Dictionary<BuildingGrade.Enum, string> buildingGradeNames = new Dictionary<BuildingGrade.Enum, string> {
                {
                    BuildingGrade.Enum.Twigs, "Twig"
                }
                , {
                    BuildingGrade.Enum.TopTier, "HQM"
                }
            }
            ;
            foreach (var prefab in GameManifest.Current.entities) {
                var gameObj = GameManager.server.FindPrefab(prefab);
                if (gameObj == null) continue;
                var thrownWep = gameObj.GetComponent<ThrownWeapon>();
                if (thrownWep != null) {
                    var itemShortname = GetItemFromPrefabShortname(thrownWep.ShortPrefabName);
                    // get item shortname from held entity
                    AddPrefabToItem(thrownWep.prefabToThrow.resourcePath, itemShortname, nameof(ThrownWeapon));
                    // assign deployed entity to same item shortname
                    PrintDebug($"  {thrownWep.ShortPrefabName}[{thrownWep.GetType()}] -> {GetPrefabShortname(thrownWep.prefabToThrow.resourcePath)} -> {itemShortname}");
                }
                var ent = gameObj.GetComponent<DecayEntity>();
                if (ent != null) {
                    var itemShortname = GetItemFromPrefabShortname(ent.ShortPrefabName);
                    if (!_decayEntityIgnoreList.ContainsKey(ent.PrefabName)) {
                        var item = ItemManager.FindItemDefinition(itemShortname);
                        var itemName = item != null ? $"{item?.displayName?.english ?? "Unknown"} ({itemShortname})" : "";
                        _decayEntityIgnoreList[ent.PrefabName] = new DecayEntityIgnoreOptions {
                            name = itemName, ignore = false, ignoreDiscord = false
                        }
                        ;
                        LogToSingleFile("decay_entity_log", $"added {ent.PrefabName} {(!string.IsNullOrEmpty(itemName) ? $"[ITEM : {
                            itemName
                        }
                        ]" : "")}");
                        saveList = true;
                    }
                    if (ent is BuildingBlock) {
                        PrintDebug($"BuildingBlock - {ent.ShortPrefabName}");
                        Type gradeType = typeof(BuildingGrade.Enum);
                        foreach(var grade in Enum.GetNames(gradeType)) {
                            var e = (BuildingGrade.Enum)Enum.Parse(gradeType, grade);
                            if (e == BuildingGrade.Enum.None || e == BuildingGrade.Enum.Count) continue;
                            var buildingBlockItemShortname = $"{ent.ShortPrefabName}.{grade.ToLower()}";
                            var gradeName = buildingGradeNames.ContainsKey(e) ? buildingGradeNames[e] : grade;
                            var prettyName = $"{gradeName} {ent.prefabAttribute.Find<Construction>(StringPool.Get(ent.PrefabName))?.info.name?.english ?? "Unknown"}";
                            _buildingBlockPrettyNames[buildingBlockItemShortname] = prettyName;
                            PrintDebug($"   BuildingGrade[{grade}] - {prettyName}[{buildingBlockItemShortname}]");
                        }
                    }
                }
            }
            if (saveList) {
                var sorted = _decayEntityIgnoreList.OrderBy(x => string.IsNullOrWhiteSpace(x.Value.name)).ThenBy(x => x.Value.name).ToDictionary(x => x.Key, x => x.Value);
                Interface.Oxide.DataFileSystem.WriteObject(decayEntityListFilename, sorted);
            }
            var saveCfg = false;
            foreach (var trackerList in _config.trackers) {
                foreach (var weaponCfg in trackerList.Value) {
                    if (!string.IsNullOrEmpty(weaponCfg.Value.name)) continue;
                    var shortname = GetItemFromPrefabShortname(weaponCfg.Key);
                    weaponCfg.Value.name = GetPrettyItemName(shortname);
                    saveCfg = true;
                }
            }
            // Don't overwrite the config if invalid since the user will lose their config!
            if (_isConfigValid && saveCfg) SaveConfig();
            Subscribe(nameof(OnPlayerDisconnected));
            Subscribe(nameof(OnMlrsFired));
            Subscribe(nameof(OnMlrsFiringEnded));
            Subscribe(nameof(OnEntitySpawned));
            Subscribe(nameof(OnEntityDeath));
            Subscribe(nameof(OnServerSave));
        }
        private void Unload() {
            var trackers = UnityEngine.Object.FindObjectsOfType<ExplosiveTracker>();
            if (trackers != null && trackers.Length > 0) {
                Puts($"Destroying {trackers.Length} explosive trackers");
                foreach (var t in trackers) {
                    t.logEvent = false;
                    UnityEngine.Object.Destroy(t);
                }
            }
            _instance = null;
            SaveRaidEventLog();
        }
        private void OnPlayerDisconnected(BasePlayer pl, string reason) {
            _verboseMode.Remove(pl.userID);
            _lastViewCommand.Remove(pl.userID);
            _notificationCooldown.Remove(pl.userID);
        }
        private void OnMlrsFired(MLRS mlrs, BasePlayer driver) {
            _MLRSRocketOwners[mlrs.transform.position] = driver.userID;
        }
        private void OnMlrsFiringEnded(MLRS mlrs) {
            _MLRSRocketOwners.Remove(mlrs.transform.position);
        }
        private void OnEntitySpawned(TimedExplosive ent) {
            if (ent == null || _ignoredTimedExplosives.Contains(ent.ShortPrefabName)) return;
            var trackerCategory = "entity_collision";
            if (!AddOrFindWeaponConfig(trackerCategory, ent.ShortPrefabName).enabled) return;
            if (ent is MLRSRocket && _MLRSRocketOwners.Count > 0)  // fix MLRS rocket creatorEntity being null when there is no driver in the MLRS truck
            {
                var mlrsOwnerID = _MLRSRocketOwners.First(x => Vector3.Distance(x.Key, ent.transform.position) < 25f).Value;
                var mlrsOwner = BasePlayer.FindByID(mlrsOwnerID);
                if (mlrsOwner != null) {
                    ent.creatorEntity = mlrsOwner;
                    ent.OwnerID = mlrsOwner.userID;
                }
            }
            var tracker = ent.gameObject.AddComponent<ExplosiveTracker>();
            if (tracker != null) {
                tracker.Init(trackerCategory);
            }
            if (_debug) {
                var pl = ent.creatorEntity as BasePlayer;
                PrintDebug($"Added explosive tracker to {ent.ShortPrefabName} spawned by {pl?.displayName ?? "Unknown"}[{pl?.userID ?? 0}]");
            }
        }
        private void OnEntityDeath(DecayEntity entity, HitInfo info) {
            if (entity == null || info == null) return;
            var initiator = info.Initiator;
            if (initiator is FireBall)  // Log fireballs spawned from inc ammo, fire arrows, etc
            {
                var fireballTrackerCategory = "entity_death_fire";
                var fireballWeaponCfg = AddOrFindWeaponConfig(fireballTrackerCategory, initiator.ShortPrefabName);
                if (!fireballWeaponCfg.enabled || IsDecayEntityIgnored(entity)) return;
                var creator = initiator.creatorEntity as BasePlayer;
                RaidEvent raidEventFireball = new RaidEvent {
                    attackerName = creator?.displayName ?? fireballWeaponCfg.name, attackerSteamID = creator?.userID ?? 0, attackerTeamID = creator?.Team?.teamID ?? 0, victimSteamID = entity.OwnerID, weapon = $"{initiator.ShortPrefabName}[{fireballTrackerCategory}]", hitEntity = $"EVENT.BURNT {GetDecayEntityShortname(entity)}", startPos = entity.transform.position + new Vector3(0, .2f, 0), endPos = entity.transform.position, timestamp = DateTime.Now
                }
                ;
                _raidEventLog.Add(raidEventFireball);
                raidEventFireball.Notify(entity);
                PrintDebug($"OnEntityDeath ({initiator.ShortPrefabName}) - WeaponPrefab: {info?.WeaponPrefab?.ShortPrefabName ?? "NULL"}, ProjectilePrefab: {info?.ProjectilePrefab?.name ?? "NULL"}");
                PrintDebug($"{initiator.ShortPrefabName} ({initiator.creatorEntity}) BURNT {GetDecayEntityShortname(entity)}[{entity?.net?.ID.Value ?? 0}]");
                return;
            }
            var attacker = info.InitiatorPlayer;
            if (attacker == null || IsDecayEntityOrAttackerIgnored(entity, attacker)) return;
            if (info?.WeaponPrefab == null && info?.damageTypes?.GetMajorityDamageType() == Rust.DamageType.Heat)  // Log fires spawned from flame throwers or other weapons
            {
                var fireTrackerCategory = "entity_death_fire";
                var fireWeapon = "fire_damage";
                var fireWeaponCfg = AddOrFindWeaponConfig(fireTrackerCategory, fireWeapon);
                if (!fireWeaponCfg.enabled) return;
                RaidEvent raidEventFire = new RaidEvent {
                    attackerName = attacker?.displayName ?? "Unknown", attackerSteamID = attacker?.userID ?? 0, attackerTeamID = attacker?.Team?.teamID ?? 0, victimSteamID = entity.OwnerID, weapon = $"{fireWeapon}[{fireTrackerCategory}]", hitEntity = $"EVENT.BURNT {GetDecayEntityShortname(entity)}", startPos = entity.transform.position + new Vector3(0, .2f, 0), endPos = entity.transform.position, timestamp = DateTime.Now
                }
                ;
                _raidEventLog.Add(raidEventFire);
                raidEventFire.Notify(entity);
                PrintDebug($"OnEntityDeath ({fireWeapon}) - WeaponPrefab: {info?.WeaponPrefab?.ShortPrefabName ?? "NULL"}, ProjectilePrefab: {info?.ProjectilePrefab?.name ?? "NULL"}");
                PrintDebug($"{fireWeapon} BURNT {GetDecayEntityShortname(entity)}[{entity?.net?.ID.Value ?? 0}]");
                return;
            }
            var weaponPrefabShortname = info?.WeaponPrefab?.ShortPrefabName;
            var projectilePrefabShortname = info?.ProjectilePrefab?.name;
            var projectileItemShortname = projectilePrefabShortname != null ? GetItemFromPrefabShortname(projectilePrefabShortname) : null;
            var heldEntity = attacker.GetHeldEntity();
            if (heldEntity is AttackEntity) {
                if (info.WeaponPrefab == null) {
                    weaponPrefabShortname = heldEntity.ShortPrefabName;
                    PrintDebug($"OnEntityDeath - WeaponPrefab is NULL! Using HeldEntity: {heldEntity.ShortPrefabName ?? "NULL"}");
                }
                var projectile = heldEntity?.GetComponent<BaseProjectile>();
                var heldProjectileItemShortname = projectile?.primaryMagazine?.ammoType?.shortname ?? null;
                if ((info.WeaponPrefab == null && info.ProjectilePrefab == null) || (projectileItemShortname != null && heldProjectileItemShortname != null && projectileItemShortname != heldProjectileItemShortname))  // certain projectiles from HitInfo do not match the projectile in the players gun Ex: ammo.pistol.hv, rifle.ammmo.hv, ammo.shotgun
                {
                    if (_debug) {
                        if (projectileItemShortname != heldProjectileItemShortname) PrintDebug($"OnEntityDeath - ProjectileItemShortname ({projectileItemShortname ?? "NULL"}) != HeldProjectileItemShortname ({heldProjectileItemShortname ?? "NULL"})! Using HeldProjectileItemShortname: {heldProjectileItemShortname ?? "NULL"}");
                        else PrintDebug($"OnEntityDeath - WeaponPrefab + ProjectilePrefab are NULL! Using HeldEntityProjectile: {projectileItemShortname ?? "NULL"}");
                    }
                    projectileItemShortname = heldProjectileItemShortname;
                }
            }
            var weaponTrackerCategory = "entity_death_weapon";
            var weaponItemShortname = weaponPrefabShortname != null ? GetItemFromPrefabShortname(weaponPrefabShortname) : null;
            var weaponEnabled = weaponItemShortname != null ? AddOrFindWeaponConfig(weaponTrackerCategory, weaponItemShortname).enabled : false;
            var projectileTrackerCategory = "entity_death_ammo";
            var projectileEnabled = projectileItemShortname != null && weaponItemShortname != projectileItemShortname ? AddOrFindWeaponConfig(projectileTrackerCategory, projectileItemShortname).enabled : false;
            PrintDebug($"OnEntityDeath - WeaponPrefab: {info?.WeaponPrefab?.ShortPrefabName ?? "NULL"} ({weaponItemShortname ?? "NULL"}), ProjectilePrefab: {info?.ProjectilePrefab?.name ?? "NULL"} ({projectileItemShortname ?? "NULL"})");
            if (!weaponEnabled && !projectileEnabled) return;
            string weapon;
            if (weaponEnabled && projectileItemShortname != null && weaponItemShortname != projectileItemShortname) weapon = $"{weaponItemShortname}[{weaponTrackerCategory}];{projectileItemShortname}[{projectileTrackerCategory}]";
            else if (projectileEnabled && weaponItemShortname != null && weaponItemShortname != projectileItemShortname) weapon = $"{projectileItemShortname}[{projectileTrackerCategory}];{weaponItemShortname}[{weaponTrackerCategory}]";
            else if (projectileEnabled) weapon = $"{projectileItemShortname}[{projectileTrackerCategory}]";
            else weapon = $"{weaponItemShortname}[{weaponTrackerCategory}]";
            var startPos = attacker.transform.position;
            startPos.y += attacker.GetHeight() -.5f;
            var endPos = info.HitPositionWorld != Vector3.zero && info.HitPositionWorld != entity.transform.position ? info.HitPositionWorld : entity.WorldSpaceBounds().ToBounds().center;
            RaidEvent raidEvent = new RaidEvent {
                attackerName = attacker?.displayName ?? "Unknown", attackerSteamID = attacker?.userID ?? 0, attackerTeamID = attacker?.Team?.teamID ?? 0, victimSteamID = entity.OwnerID, weapon = weapon, hitEntity = $"EVENT.DESTROYED {GetDecayEntityShortname(entity)}", startPos = startPos, endPos = endPos, timestamp = DateTime.Now
            }
            ;
            _raidEventLog.Add(raidEvent);
            raidEvent.Notify(entity);
            PrintDebug($"{weapon} DESTROYED {GetDecayEntityShortname(entity)}[{entity?.net?.ID.Value ?? 0}]");
        }
        private void OnServerSave() {
            SaveRaidEventLog();
        }
        private void OnNewSave(string filename) {
            _wipeData = true;
        }
#endregion
#region Commands
        [Command("x")]
        private void ViewExplosionsCommand(IPlayer player, string command, string[] args) {
            BasePlayer pl = player.Object as BasePlayer;
            if (pl == null || !pl.IsAdmin) return;
            if (args.Length > 0 && (args[0].ToLower() == "v" || args[0].ToLower() == "extra")) {
                _verboseMode[pl.userID] = _verboseMode.ContainsKey(pl.userID) ? !_verboseMode[pl.userID] : true;
                SendChatMsg(pl, lang.GetMessage(_verboseMode[pl.userID] ? "ViewEventsCommand.ExtraModeEnabled" : "ViewEventsCommand.ExtraModeDisabled", this, pl.UserIDString));
                return;
            }
            float radius = _config.searchRadius > 0f ? _config.searchRadius : 50f;
            string filterType = args.Length > 0 ? args[0].ToLower() : "";
            string filter = "";
            bool verbose = _verboseMode.ContainsKey(pl.userID) && _verboseMode[pl.userID];
            Vector3 pos = pl.transform.position;
            pos.y += pl.GetHeight() -.5f;
            float tempRadius;
            IEnumerable<IGrouping<RaidFilter, RaidEvent>> groupedRaidsNearMe;
            switch (filterType) {
                case "l" : case "last" : string[] lastArgs;
                if (_lastViewCommand.TryGetValue(pl.userID, out lastArgs)) ViewExplosionsCommand(player, command, lastArgs);
                return;
                case "help" : _sb.Clear();
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpHeader", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpDefault", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpExtraMode", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpWipe", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpLast", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilter", this, pl.UserIDString));
                _sb.Append("<indent=6>");
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilterTime", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilterWeapon", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilterEntity", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilterTeam", this, pl.UserIDString));
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpFilterPlayer", this, pl.UserIDString));
                _sb.Append("<indent=0>");
                _sb.AppendLine(lang.GetMessage("ViewEventsCommand.HelpPrintRaidEvent", this, pl.UserIDString));
                SendChatMsg(pl, _sb.ToString(), "");
                return;
                case "wipe" : if (!permission.UserHasPermission(pl.UserIDString, PERM_WIPE)) {
                    SendChatMsg(pl, lang.GetMessage("ViewEventsCommand.WipePermission", this, pl.UserIDString));
                    return;
                }
                if (args.Length > 1 && float.TryParse(args[1], out tempRadius)) radius = tempRadius;
                var raidEventsToWipe = FindRaidEventsInSphere(pos, radius).ToList();
                if (raidEventsToWipe.Count == 0) {
                    SendChatMsg(pl, string.Format(lang.GetMessage("ViewEventsCommand.NotFoundRadius", this, pl.UserIDString), radius));
                    return;
                }
                foreach(var raidEvent in raidEventsToWipe) {
                    int idx = raidEvent.GetIndex();
                    if (idx > -1) _raidEventLog.RemoveAt(idx);
                }
                var playerPos = pl.transform.position;
                var gridPos = PhoneController.PositionToGridCoord(playerPos);
                var filename = $"{Name}\\WipedRaidEvents\\{string.Format(" {
                    0 : yyyy-MM-dd
                }
                ", DateTime.Now)}\\{pl.userID}\\{gridPos}_{string.Format(" {
                    0 : h-mm-tt
                }
                ", DateTime.Now)}";
                LogToFile("wiped_raid_events", $"{pl.displayName}[{pl.userID}] wiped {raidEventsToWipe.Count} raid events in {gridPos} ({FormatPosition(playerPos)})", this);
                Interface.Oxide.DataFileSystem.WriteObject(filename, raidEventsToWipe);
                SaveRaidEventLog();
                SendChatMsg(pl, string.Format(lang.GetMessage("ViewEventsCommand.WipedRaidEventsRadius", this, pl.UserIDString), raidEventsToWipe.Count, radius, gridPos));
                return;
                case "time" : filter = args.Length > 1 ? args[1].ToLower() : "";
                if (args.Length > 2 && float.TryParse(args[2], out tempRadius)) radius = tempRadius;
                double hours;
                if (!double.TryParse(filter, out hours)) hours = 24;
                groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).Where(x => DateTime.Now.Subtract(x.timestamp).TotalHours <= hours).GroupBy(x => new RaidFilter {
                    filter = $"{x.attackerName}[{x.attackerSteamID}]", filterType = filterType
                }
                );
                break;
                case "weapon" : filter = args.Length > 1 ? args[1].ToLower() : "";
                if (args.Length > 2 && float.TryParse(args[2], out tempRadius)) radius = tempRadius;
                groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).Where(x => x.weapon.Contains(filter) || FindWeaponConfig(x.GetTrackerCategory(), x.GetPrimaryWeaponShortname()).name.ToLower().Contains(filter)).GroupBy(x => new RaidFilter {
                    filter = FindWeaponConfig(x.GetTrackerCategory(), x.GetPrimaryWeaponShortname()).name, filterType = filterType
                }
                );
                break;
                case "entity" : filter = args.Length > 1 ? args[1].ToLower() : "";
                if (args.Length > 2 && float.TryParse(args[2], out tempRadius)) radius = tempRadius;
                groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).Where(x => x.hitEntity.Contains(filter)).GroupBy(x => new RaidFilter {
                    filter = x.GetHitEntityShortname(), filterType = filterType
                }
                );
                break;
                case "team" : filter = args.Length > 1 ? args[1].ToLower() : "";
                if (args.Length > 2 && float.TryParse(args[2], out tempRadius)) radius = tempRadius;
                IEnumerable<RaidEvent> tempRaidEvents = FindRaidEventsInSphere(pos, radius);
                if (!string.IsNullOrEmpty(filter)) tempRaidEvents = tempRaidEvents.Where(x => x.attackerTeamID.ToString() == filter);
                groupedRaidsNearMe = tempRaidEvents.GroupBy(x => {
                    var team = RelationshipManager.ServerInstance.FindTeam(x.attackerTeamID);
                    return new RaidFilter {
                        filter = team != null ? $"{team.GetLeader()?.displayName ?? "UNKNOWN LEADER"}'s Team (ID: {x.attackerTeamID})" : x.attackerTeamID.ToString(), filterType = filterType
                    }
                    ;
                }
                );
                break;
                case "player" : filter = args.Length > 1 ? args[1].ToLower() : "";
                if (args.Length > 2 && float.TryParse(args[2], out tempRadius)) radius = tempRadius;
                groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).Where(x => x.attackerSteamID.ToString() == filter || x.attackerName.ToLower().Contains(filter)).GroupBy(x => new RaidFilter {
                    filter = $"{x.attackerName}[{x.attackerSteamID}]", filterType = filterType
                }
                );
                break;
                default : if (args.Length > 0 && float.TryParse(args[0], out tempRadius)) radius = tempRadius;
                groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).GroupBy(x => new RaidFilter {
                    filter = $"{x.attackerName}[{x.attackerSteamID}]", filterType = "default"
                }
                );
                break;
            }
            DrawRaidEvents(pl, groupedRaidsNearMe, filterType, filter, radius);
            _lastViewCommand[pl.userID] = args;
        }
        [Command("px")]
        private void PlayerViewExplosionsCommand(IPlayer player, string command, string[] args) {
            BasePlayer pl = player.Object as BasePlayer;
            if (pl == null) return;
            bool isAdmin = player.IsAdmin;
            if (!isAdmin && !permission.UserHasPermission(pl.UserIDString, PERM_PX)) {
                SendChatMsg(pl, string.Format(lang.GetMessage("ViewEventsCommand.NoPermission", this, pl.UserIDString), $"/{command}"));
                return;
            }
            try {
                if (!isAdmin) {
                    pl.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
                    pl.SendNetworkUpdateImmediate();
                }
                float radius = _config.searchRadius > 0f ? _config.searchRadius : 50f;
                float tempRadius;
                if (args.Length > 0 && float.TryParse(args[0], out tempRadius)) radius = Mathf.Clamp(tempRadius, 0f, 100f);
                Vector3 pos = pl.transform.position;
                pos.y += pl.GetHeight() -.5f;
                var groupedRaidsNearMe = FindRaidEventsInSphere(pos, radius).Where(x => x.victimSteamID == pl.userID || (pl.Team != null && pl.Team.members.Contains(x.victimSteamID)) || FindWeaponConfig(x.GetTrackerCategory(), x.GetPrimaryWeaponShortname()).alwaysLog).Where(x => DateTime.Now.Subtract(x.timestamp).Minutes > _config.playerViewExplosionsCommand.ignoreRaidEventsLessThanMinutes).GroupBy(x => new RaidFilter {
                    filter = $"{x.attackerSteamID.GetHashCode()}", filterType = "victim"
                }
                );
                DrawRaidEvents(pl, groupedRaidsNearMe, "victim", pl.UserIDString, radius, _config.playerViewExplosionsCommand.drawAttackerName);
            }
            catch (Exception ex) {
                LogToSingleFile("px_error_log", $"error drawing player explosions for {pl.userID}[{pl?.displayName ?? "Unknown"}]\n\nException: {ex.Message}");
            }
            if (!isAdmin) {
                pl.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
                pl.SendNetworkUpdateImmediate();
            }
        }
        [Command("re")]
        private void RaidEventDetailsCommand(IPlayer player, string command, string[] args) {
            BasePlayer pl = player.Object as BasePlayer;
            if (pl == null || !pl.IsAdmin) return;
            int index = -1;
            int i;
            if (args.Length > 0 && int.TryParse(args[0], out i)) index = i;
            if (index < _raidEventLog.Count && _raidEventLog[index] != null) {
                RaidEvent raidEvent = _raidEventLog[index];
                SendChatMsg(pl, $"\n\nRaid Event {index}:\n{JsonConvert.SerializeObject(raidEvent, Formatting.Indented)}");
            }
            else SendChatMsg(pl, $"Raid event {index} not found!");
        }
        [Command("rt.debug")]
        private void DebugCommand(IPlayer player, string command, string[] args) {
            if (!player.IsServer) return;
            _debug = !_debug;
            Puts($"debug: {_debug}");
        }
        [Command("rt.weapon_colors")]
        private void WeaponColorsCommand(IPlayer player, string command, string[] args) {
            BasePlayer pl = player.Object as BasePlayer;
            if (pl == null || !pl.IsAdmin) return;
            _sb.Clear();
            var category = args.Length > 0 && _config.trackers.ContainsKey(args[0].ToLower()) ? args[0].ToLower() : _config.trackers.Keys.First();
            foreach (var weaponCfg in _config.trackers[category]) {
                var val = weaponCfg.Value;
                _sb.AppendLine($"<color={val.hexColor}>• {val.name} ({category})</color> - Enabled: {val.enabled}");
            }
            SendChatMsg(pl, $"\n\n{_sb}");
        }
#endregion
#region Helpers
        private void DrawRaidEvents(BasePlayer pl, IEnumerable<IGrouping<RaidFilter, RaidEvent>> groupedRaidsNearMe, string filterType, string filter, float radius, bool drawAttackerName = true) {
            bool verbose = _verboseMode.ContainsKey(pl.userID) && _verboseMode[pl.userID];
            int raidCount = groupedRaidsNearMe.Sum(x => x.Count());
            _sb.Clear();
            _sb.AppendLine(string.Format(lang.GetMessage("ViewEventsCommand.Header", this, pl.UserIDString), raidCount, radius));
            if (!string.IsNullOrEmpty(filterType)) _sb.AppendLine(string.Format(lang.GetMessage("ViewEventsCommand.Filter", this, pl.UserIDString), filterType, filter));
            var groupCloseNames = new Dictionary<ulong, List<Vector3>>();
            foreach (var grouping in groupedRaidsNearMe) {
                var groupingColor = GetRandomColor();
                var groupingColorHex = GetHexColor(groupingColor);
                foreach (RaidEvent raidEvent in grouping) {
                    if (raidEvent.attackerTeamID > 0 && !_teamColors.ContainsKey(raidEvent.attackerTeamID)) {
                        _teamColors[raidEvent.attackerTeamID] = _uniqueColors[_currentTeamColorIdx];
                        _currentTeamColorIdx = _currentTeamColorIdx < _uniqueColors.Count() ? _currentTeamColorIdx : -1;
                        _currentTeamColorIdx++;
                    }
                    var teamColor = raidEvent.attackerTeamID > 0 ? _teamColors[raidEvent.attackerTeamID] : GetRandomColor();
                    var teamColorHex = GetHexColor(teamColor);
                    var weaponCfg = FindWeaponConfig(raidEvent.GetTrackerCategory(), raidEvent.GetPrimaryWeaponShortname());
                    var weaponColor = weaponCfg.GetWeaponColor();
                    if (filterType == "weapon") {
                        groupingColor = weaponColor;
                        groupingColorHex = GetHexColor(groupingColor);
                    }
                    else if (filterType == "team") {
                        groupingColor = teamColor;
                        groupingColorHex = GetHexColor(groupingColor);
                    }
                    string startText, endText;
                    var startPos = raidEvent.startPos;
                    var endPos = raidEvent.endPos;
                    if (weaponCfg.shortArrow) startPos = raidEvent.endPos + new Vector3(0, .2f, 0);
                    string attackerTeam = raidEvent.attackerTeamID > 0 ? string.Format(lang.GetMessage("ViewEventsCommand.Team", this, pl.UserIDString), teamColorHex, raidEvent.attackerTeamID) : "";
                    if (verbose) {
                        startText = string.Format(lang.GetMessage("ViewEventsCommand.StartTextExtra", this, pl.UserIDString), drawAttackerName ? raidEvent.attackerName : "X", raidEvent.attackerSteamID, attackerTeam);
                        endText = string.Format(lang.GetMessage("ViewEventsCommand.EndTextExtra", this, pl.UserIDString), groupingColorHex, raidEvent.GetIndex(), raidEvent.GetWeaponName(), raidEvent.hitEntity.Replace(raidEvent.GetEventType(), raidEvent.GetPrettyEventType()));
                    }
                    else {
                        startText = string.Format(lang.GetMessage("ViewEventsCommand.StartText", this, pl.UserIDString), drawAttackerName ? raidEvent.attackerName : "X", attackerTeam);
                        endText = string.Format(lang.GetMessage("ViewEventsCommand.EndText", this, pl.UserIDString), groupingColorHex, raidEvent.GetWeaponName());
                    }
                    if (raidEvent.hitEntity.Contains("EVENT.ATTACHED")) Box(pl, endPos, .05f, groupingColor, _config.drawDuration);
                    else if (raidEvent.hitEntity.Contains("EVENT.HIT")) Sphere(pl, endPos, .05f, groupingColor, _config.drawDuration);
                    if (!groupCloseNames.ContainsKey(raidEvent.attackerSteamID)) groupCloseNames[raidEvent.attackerSteamID] = new List<Vector3>();
                    if (!groupCloseNames[raidEvent.attackerSteamID].Any(x => Vector3.Distance(x, startPos) <.1f)) {
                        groupCloseNames[raidEvent.attackerSteamID].Add(startPos);
                        Text(pl, startPos, startText, groupingColor, _config.drawDuration);
                    }
                    Arrow(pl, startPos, endPos, .05f, groupingColor, _config.drawDuration);
                    Text(pl, endPos + new Vector3(0, .05f, 0), endText, weaponColor, _config.drawDuration);
                }
                _sb.AppendLine(string.Format(lang.GetMessage("ViewEventsCommand.GroupingCount", this, pl.UserIDString), groupingColorHex, grouping.Count(), grouping.Key.filterType, grouping.Key.filter));
                var weaponCounts = grouping.GroupBy(x => new {
                    weapon = x.GetPrimaryWeaponShortname(), trackerCategory = x.GetTrackerCategory()
                }
                ).Select(x => new {
                    data = x.Key, count = x.Count()
                }
                );
                _sb.Append("<indent=6>");
                foreach (var x in weaponCounts) {
                    var weaponCfg = FindWeaponConfig(x.data.trackerCategory, x.data.weapon);
                    _sb.Append(string.Format(lang.GetMessage("ViewEventsCommand.WeaponCount", this, pl.UserIDString), weaponCfg.hexColor, x.count, weaponCfg.name, x.data.trackerCategory));
                }
                _sb.Append("<indent=0>");
                _sb.AppendLine($"\n");
            }
            if (raidCount < 1) _sb.AppendLine(lang.GetMessage("ViewEventsCommand.NotFound", this, pl.UserIDString));
            SendChatMsg(pl, _sb.ToString().TrimEnd(), "");
        }
        private void LogToSingleFile(string filename, string text) => LogToFile(filename, string.Format("[{0:yyyy-MM-dd HH:mm:ss}] {1}", DateTime.Now, text), this, false);
        private void PrintDebug(string msg) {
            if (_debug) Puts($"[DEBUG] {msg}");
        }
        private void SendChatMsg(BasePlayer pl, string msg, string prefix = null) {
            var p = prefix != null ? prefix : lang.GetMessage("ChatPrefix", this, pl.UserIDString);
            _rustPlayer.Message(pl, msg, p, _config.chatIconID, Array.Empty<object>());
            if (_config.printToClientConsole) pl.ConsoleMessage($"{(!string.IsNullOrEmpty(p) ? $" {
                p
            }
            " : "")}{msg}");
        }
        public void Arrow(BasePlayer player, Vector3 from, Vector3 to, float headSize, Color color, float duration) => player.SendConsoleCommand("ddraw.arrow", duration, color, from, to, headSize);
        public void Sphere(BasePlayer player, Vector3 pos, float radius, Color color, float duration) => player.SendConsoleCommand("ddraw.sphere", duration, color, pos, radius);
        public void Box(BasePlayer player, Vector3 pos, float size, Color color, float duration) => player.SendConsoleCommand("ddraw.box", duration, color, pos, size);
        public void Text(BasePlayer player, Vector3 pos, string text, Color color, float duration) => player.SendConsoleCommand("ddraw.text", duration, color, pos, text);
        private void AddPrefabToItem(string prefab, string itemShortname, string prefabSource) {
            var prefabShortname = GetPrefabShortname(prefab);
            if (_prefabToItem.ContainsKey(prefabShortname)) return;
            _prefabToItem[prefabShortname] = itemShortname;
            PrintDebug($"prefabToItem - {prefabSource}: {prefabShortname} -> {itemShortname}");
        }
        private string GetItemFromPrefabShortname(string prefabShortname) => _prefabToItem.ContainsKey(prefabShortname) ? _prefabToItem[prefabShortname] : prefabShortname;
        private string GetPrefabShortname(string prefab) => prefab.Substring(prefab.LastIndexOf('/') + 1).Replace(".prefab", "");
        private string GetPrettyItemName(string itemShortname) {
            string buildingBlockName;
            if (_buildingBlockPrettyNames.TryGetValue(itemShortname, out buildingBlockName)) return buildingBlockName;
            return ItemManager.FindItemDefinition(itemShortname)?.displayName?.english ?? itemShortname;
        }
        private IEnumerable<RaidEvent> FindRaidEventsInSphere(Vector3 pos, float r) => _raidEventLog.Where(x => Vector3.Distance(pos, x.startPos) < r || Vector3.Distance(pos, x.endPos) < r);
        private Color GetRandomColor() => UnityEngine.Random.ColorHSV(0f, 1f, .4f, .8f, .5f, 1f);
        private string GetHexColor(Color color) => $"#{ColorUtility.ToHtmlStringRGB(color)}";
        private PluginConfig.WeaponConfig AddOrFindWeaponConfig(string category, string shortname) {
            if (!_config.trackers.ContainsKey(category)) _config.trackers[category] = new SortedDictionary<string, PluginConfig.WeaponConfig>();
            if (!_config.trackers[category].ContainsKey(shortname)) {
                var weaponCfg = new PluginConfig.WeaponConfig {
                    enabled = _config.enableNewTrackers, name = GetPrettyItemName(GetItemFromPrefabShortname(shortname)), hexColor = GetHexColor(GetRandomColor())
                }
                ;
                _config.trackers[category][shortname] = weaponCfg;
                LogToSingleFile("weapon_config_log", $"added {weaponCfg.name} ({category} / {shortname}), enabled: {weaponCfg.enabled}");
                SaveConfig();
            }
            return FindWeaponConfig(category, shortname);
        }
        private PluginConfig.WeaponConfig FindWeaponConfig(string category, string shortname) {
            PluginConfig.WeaponConfig weaponCfg;
            if (_config.trackers.ContainsKey(category) && _config.trackers[category].TryGetValue(shortname, out weaponCfg)) {
                if (_dev) {
                    return new PluginConfig.WeaponConfig {
                        enabled = true, name = weaponCfg.name, hexColor = weaponCfg.hexColor, alwaysLog = weaponCfg.alwaysLog, shortArrow = weaponCfg.shortArrow, discordIcon = weaponCfg.discordIcon, notifyConsole = true, notifyAdmin = true, notifyDiscord = true, logToFile = true
                    }
                    ;
                }
                PluginConfig.WeaponConfig globalWeaponCfg = null;
                if (_config.trackers.ContainsKey("_global") && _config.trackers["_global"].ContainsKey("*")) globalWeaponCfg = _config.trackers["_global"]["*"];
                if ((globalWeaponCfg == null || !globalWeaponCfg.enabled) && _config.trackers[category].ContainsKey("*")) globalWeaponCfg = _config.trackers[category]["*"];
                if (!weaponCfg.enabled && globalWeaponCfg.enabled) {
                    return new PluginConfig.WeaponConfig {
                        enabled = globalWeaponCfg.enabled, name = weaponCfg.name, hexColor = weaponCfg.hexColor, alwaysLog = weaponCfg.alwaysLog, shortArrow = weaponCfg.shortArrow, discordIcon = weaponCfg.discordIcon, notifyConsole = globalWeaponCfg.notifyConsole, notifyAdmin = globalWeaponCfg.notifyAdmin, notifyDiscord = globalWeaponCfg.notifyDiscord, logToFile = globalWeaponCfg.logToFile
                    }
                    ;
                }
                return weaponCfg;
            }
            throw new Exception($"THIS SHOULD NEVER HAPPEN! Unable to find weapon config [{category} / {shortname}]");
        }
        private string GetDecayEntityShortname(DecayEntity entity) {
            var buildingBlock = entity as BuildingBlock;
            return $"{entity.ShortPrefabName}{(buildingBlock != null ? $". {
                buildingBlock.grade.ToString().ToLower()
            }
            " : "")}";
        }
        private bool IsDecayEntityIgnored(DecayEntity entity) {
            if (entity is LootContainer || entity.OwnerID == 0) return true;
            if (!_dev && _decayEntityIgnoreList.ContainsKey(entity.PrefabName) && _decayEntityIgnoreList[entity.PrefabName].ignore) return true;
            var buildingBlock = entity as BuildingBlock;
            bool ignoreGrade;
            if (!_dev && buildingBlock != null && _config.ignoreBuildingGrades.TryGetValue(buildingBlock.grade, out ignoreGrade) && ignoreGrade) return true;
            if (Convert.ToBoolean(RaidableBases?.Call("EventTerritory", entity.transform.position))) return true;
            if (Convert.ToBoolean(AbandonedBases?.Call("EventTerritory", entity.transform.position))) return true;
            return false;
        }
        private bool IsDecayEntityOrAttackerIgnored(DecayEntity entity, BasePlayer attacker) {
            if (IsDecayEntityIgnored(entity)) return true;
            if (attacker != null && attacker.IsAdmin) return false;
            if (!_dev && _config.ignoreSameOwner && attacker != null && attacker.userID == entity.OwnerID) return true;
            if (!_dev && _config.ignoreTeamMember && attacker != null && attacker.Team != null && attacker.Team.members.Contains(entity.OwnerID)) return true;
            if (!_dev && _config.ignoreClanMemberOrAlly && attacker != null && Convert.ToBoolean(Clans?.Call("IsMemberOrAlly", attacker.UserIDString, entity.OwnerID.ToString()))) return true;
            return false;
    
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху