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

FREE TruePVE : 2.3.3 2.3.3

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using Rust.Ai.Gen2;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins {
    [Info("TruePVE", "nivex", "2.3.3")]
    [Description("Improvement of the default Rust PVE behavior")]
    // Thanks to the original author, ignignokt84.internal class TruePVE : RustPlugin
    {
#region Variables
        // config/data container
        private Configuration config = new();
        [PluginReference]
        Plugin ZoneManager, LiteZones, Clans, Friends, AbandonedBases, RaidableBases;
        public string usageString;
        public enum Command {
            def, sched, trace, usage, enable, sleepers
        }
        ;
        public enum DamageResult {
            None, Allow, Block
        }
        [Flags]
        public enum RuleFlags : ulong {
            None = 0, AdminsHurtSleepers = 1uL << 1, AdminsHurtPlayers = 1uL << 2, AnimalsIgnoreSleepers = 1uL << 3, AuthorizedDamage = 1uL << 4, AuthorizedDamageRequiresOwnership = 1uL << 5, CupboardOwnership = 1uL << 6, FriendlyFire = 1uL << 7, HeliDamageLocked = 1uL << 8, HumanNPCDamage = 1uL << 9, LockedBoxesImmortal = 1uL << 10, LockedDoorsImmortal = 1uL << 11, NoPlayerDamageToCar = 1uL << 12, NoPlayerDamageToMini = 1uL << 13, NoPlayerDamageToScrap = 1uL << 14, NoHeliDamage = 1uL << 15, NoHeliDamagePlayer = 1uL << 16, NoHeliDamageQuarry = 1uL << 17, NoHeliDamageRidableHorses = 1uL << 18, NoHeliDamageSleepers = 1uL << 19, NoMLRSDamage = 1uL << 20, NpcsCanHurtAnything = 1uL << 21, PlayerSamSitesIgnorePlayers = 1uL << 22, ProtectedSleepers = 1uL << 23, TrapsIgnorePlayers = 1uL << 24, TrapsIgnoreScientist = 1uL << 25, TurretsIgnorePlayers = 1uL << 26, TurretsIgnoreScientist = 1uL << 27, StaticTurretsIgnoreScientist = 1uL << 28, TwigDamage = 1uL << 29, TwigDamageRequiresOwnership = 1uL << 30, VehiclesTakeCollisionDamageWithoutDriver = 1uL << 31, SamSitesIgnoreMLRS = 1uL << 32, SelfDamage = 1uL << 33, StaticSamSitesIgnorePlayers = 1uL << 34, StaticTurretsIgnorePlayers = 1uL << 35, SafeZoneTurretsIgnorePlayers = 1uL << 36, SuicideBlocked = 1uL << 37, NoHeliDamageBuildings = 1uL << 38, WoodenDamage = 1uL << 39, WoodenDamageRequiresOwnership = 1uL << 40, AuthorizedDamageCheckPrivilege = 1uL << 41, ExcludeTugboatFromImmortalFlags = 1uL << 42, LockedVehiclesImmortal = 1uL << 43, TurretsIgnoreBradley = 1uL << 44, AuthorizedFarmableDamage = 1uL << 45, HopperCannotTargetEnemyLoot = 1uL << 46
        }
        private bool IsUnloading;
        private Timer scheduleUpdateTimer;
        // timer to check for schedule updates
        private bool shareRedirectDudEnabled;
        // undocumented.UAYOR.private RuleSet dudRuleSet;                                     // dud ruleset when no locations are shared
        private RuleSet currentRuleSet;
        // current ruleset
        private string currentBroadcastMessage;
        // current broadcast message
        private bool useZones;
        // internal useZones flag
        private const string Any = "any";
        // constant "any" string for rules
        private const string AllZones = "allzones";
        // constant "allzones" string for mappings
        private const string PermCanMap = "truepve.canmap";
        // permission for mapping command
        private bool animalsIgnoreSleepers;
        // toggle flag to protect sleepers
        private bool trace;
        // trace flag
        private const string traceFile = "ruletrace";
        // tracefile name
        private const float traceTimeout = 300f;
        // auto-disable trace after 300s (5m)
        private Timer traceTimer;
        // trace timeout timer
        private bool tpveEnabled = true;
        // toggle flag for damage handling
        private List<DamageType> _damageTypes = new() {
            DamageType.Arrow, DamageType.Blunt, DamageType.Bullet, DamageType.Explosion, DamageType.Cold, DamageType.Heat, DamageType.Generic, DamageType.Slash, DamageType.Stab,
        }
        ;
        private uint maincannonshell = 3032863244;
        private uint trainbarricade = 1221760186;
        private uint trainbarricadeheavy = 1363243026;
        private uint loot_trash = 3279100614;
        private uint giftbox_loot = 2216891097;
        private uint campfire = 4160694184;
        private uint oilfireballsmall = 3550347674;
        private uint heli_napalm = 184893264;
        private uint rocket_heli_napalm = 200672762;
        private uint rocket_heli = 129320027;
        private bool excludeAllZones;
        private readonly List<ulong> _waiting = new();
        private readonly HashSet<string> _deployables = new();
        private readonly HashSet<string> exclusionLocationsSet = new(StringComparer.OrdinalIgnoreCase);
        private readonly Dictionary<ulong, List<PlayerExclusion>> playerDelayExclusions = new();
        private readonly Dictionary<string, RuleSet> ruleSetByNameDictionary = new(StringComparer.OrdinalIgnoreCase);
        private class PlayerExclusion : Pool.IPooled {
            public Plugin plugin;
            public float time;
            public bool IsExpired => Time.time > time;
            public void EnterPool() {
                plugin = null;
                time = 0f;
            }
            public void LeavePool() {
            }
        }
#endregion
#region Loading/Unloading
        protected new static void Puts(string format, params object[] args) {
            if (!string.IsNullOrEmpty(format)) {
                Interface.Oxide.LogInfo("[{0}] {1}", "TruePVE", (args.Length != 0) ? string.Format(format, args) : format);
            }
        }
        private void Unload() {
            IsUnloading = true;
            scheduleUpdateTimer?.Destroy();
            SaveData();
        }
        private void OnPluginLoaded(Plugin plugin) {
            if (plugin.Name == "ZoneManager") ZoneManager = plugin;
            if (plugin.Name == "LiteZones") LiteZones = plugin;
            if (ZoneManager != null || LiteZones != null) SetUseZones();
        }
        private void OnPluginUnloaded(Plugin plugin) {
            if (plugin.Name == "ZoneManager") ZoneManager = null;
            if (plugin.Name == "LiteZones") LiteZones = null;
            if (ZoneManager == null && LiteZones == null) useZones = false;
        }
        private void OnCreatedDynamicPVP() => SetUseZones();
        private void OnDeletedDynamicPVP() => SetUseZones();
        protected void SetUseZones() {
            useZones = config != null && config.mappings != null && config.options != null && config.options.useZones && (LiteZones != null || ZoneManager != null);
            if (useZones && config.mappings.Count == 1) {
                foreach (var mapping in config.mappings) {
                    useZones = !mapping.Key.Equals(config.defaultRuleSet);
                }
            }
        }
        private void Init() {
            if (!config.options.Loot.NoShieldDrop) {
                Unsubscribe(nameof(OnPlayerActiveShieldDrop));
            }
            if (!config.options.Loot.NoActiveItemDrop) {
                Unsubscribe(nameof(OnPlayerDropActiveItem));
            }
            if (!config.options.Loot.NoRustBackpackDrop) {
                Unsubscribe(nameof(OnBackpackDrop));
            }
            if (!config.BlockSprayCanInSafeZones) {
                Unsubscribe(nameof(OnSprayCreate));
            }
            Unsubscribe(nameof(CanLootEntity));
            Unsubscribe(nameof(OnCodeEntered));
            Unsubscribe(nameof(OnCupboardAuthorize));
            Unsubscribe(nameof(CanHelicopterStrafeTarget));
            Unsubscribe(nameof(CanWaterBallSplash));
            Unsubscribe(nameof(OnEntityMarkHostile));
            Unsubscribe(nameof(OnEntitySpawned));
            Unsubscribe(nameof(OnEntityEnter));
            Unsubscribe(nameof(OnTurretTarget));
            Unsubscribe(nameof(OnTimedExplosiveExplode));
            Unsubscribe(nameof(OnWallpaperRemove));
            Unsubscribe(nameof(OnEntityTakeDamage));
            Unsubscribe(nameof(OnPlayerConnected));
            Unsubscribe(nameof(OnSamSiteTarget));
            Unsubscribe(nameof(OnTrapTrigger));
            Unsubscribe(nameof(OnMlrsFire));
            // register console commands automagically
            foreach (Command command in Enum.GetValues(typeof(Command))) {
                AddCovalenceCommand($"tpve.{command}", nameof(CommandDelegator));
            }
            // register chat commands
            AddCovalenceCommand("tpve_prod", nameof(CommandDelegator));
            AddCovalenceCommand("tpve_enable", nameof(CommandDelegator));
            AddCovalenceCommand("tpve", nameof(CommandDelegator));
            permission.RegisterPermission(PermCanMap, this);
            // build usage string for console (without sizing)
            usageString = WrapColor("orange", GetMessage("Header_Usage")) + $" - {Version}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.def}") + $" - {GetMessage("Cmd_Usage_def")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.trace}") + $" - {GetMessage("Cmd_Usage_trace")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.sched} [enable|disable]") + $" - {GetMessage("Cmd_Usage_sched")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve_prod") + $" - {GetMessage("Cmd_Usage_prod")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve map") + $" - {GetMessage("Cmd_Usage_map")}";
            LoadData();
        }
        private bool IsAnimalsIgnoringSleepers() => animalsIgnoreSleepers || config.ruleSets.Exists(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers));
        private bool IsAnimalsIgnoringSleepers(RuleSet ruleSet) => animalsIgnoreSleepers || ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers);
        private void OnServerInitialized(bool isStartup) {
            isServerStartingUp = false;
            // check for server pve setting
            if (ConVar.Server.pve) WarnPve();
            // load configuration
            config.Init(this);
            currentRuleSet = config.GetDefaultRuleSet();
            dudRuleSet = config.GetDudRuleSet();
            if (currentRuleSet == null) Puts(GetMessage("Warning_NoRuleSet"), config.defaultRuleSet);
            SetUseZones();
            if (config.schedule.enabled) {
                TimerLoop(true);
            }
            if (!IsAnimalsIgnoringSleepers()) {
                Unsubscribe(nameof(OnNpcTarget));
            }
            if (config.PreventSafeZoneStrafing) {
                Subscribe(nameof(CanHelicopterStrafeTarget));
            }
            if (config.PreventThrowingWaterInFreezingBiome || config.BlockRadioactiveWaterDamage) {
                Subscribe(nameof(CanWaterBallSplash));
            }
            if (currentRuleSet == null) {
                return;
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SafeZoneTurretsIgnorePlayers | RuleFlags.StaticTurretsIgnorePlayers | RuleFlags.StaticTurretsIgnoreScientist | RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist | RuleFlags.TurretsIgnorePlayers | RuleFlags.TurretsIgnoreScientist | RuleFlags.TurretsIgnoreBradley)) != 0)) {
                Subscribe(nameof(OnEntityEnter));
                Subscribe(nameof(OnTurretTarget));
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SamSitesIgnoreMLRS | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.StaticSamSitesIgnorePlayers)) != 0)) {
                Subscribe(nameof(OnSamSiteTarget));
            }
            if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist)) != 0)) {
                Subscribe(nameof(OnTrapTrigger));
            }
            if (config.schedule.enabled && config.schedule.broadcast && !string.IsNullOrEmpty(currentBroadcastMessage)) {
                Subscribe(nameof(OnPlayerConnected));
            }
            if (config.options.disableBaseOvenSplash) {
                ServerMgr.Instance.StartCoroutine(OvenCo());
            }
            if (config.options.disableHostility) {
                Subscribe(nameof(OnEntityMarkHostile));
            }
            RuleSet ruleSet = currentRuleSet;
            if (config.options.handleDamage && ruleSet != null && !ruleSet.IsEmpty() && ruleSet.enabled) {
                Subscribe(nameof(OnEntityTakeDamage));
                tpveEnabled = true;
            }
            if (config.wallpaper) {
                Subscribe(nameof(OnWallpaperRemove));
                Subscribe(nameof(OnTimedExplosiveExplode));
            }
            if (config.options.Loot.ProtectTC) {
                Subscribe(nameof(OnCupboardAuthorize));
                Subscribe(nameof(CanLootEntity));
            }
            else if (config.options.Loot.Lifts) {
                Subscribe(nameof(CanLootEntity));
            }
            Subscribe(nameof(OnEntitySpawned));
            Subscribe(nameof(OnMlrsFire));
            BuildPrefabIds();
            AllowLocksOnContainers();
        }
        private IEnumerator OvenCo() {
            int checks = 0;
            foreach (var ent in BaseNetworkable.serverEntities) {
                if (++checks > 500) {
                    checks = 0;
                    yield return null;
                }
                if (ent is BaseOven oven) {
                    oven.disabledBySplash = false;
                }
            }
        }
        private void BuildPrefabIds() {
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/m2bradley/maincannonshell.prefab", out var prefab1)) maincannonshell = prefab1;
            if (StringPool.toNumber.TryGetValue("assets/content/props/train_tunnels/trainbarricade.prefab", out var prefab2)) trainbarricade = prefab2;
            if (StringPool.toNumber.TryGetValue("assets/content/props/train_tunnels/trainbarricadeheavy.prefab", out var prefab3)) trainbarricadeheavy = prefab3;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/radtown/loot_trash.prefab", out var prefab4)) loot_trash = prefab4;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/misc/xmas/giftbox/giftbox_loot.prefab", out var prefab5)) giftbox_loot = prefab5;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/deployable/campfire/campfire.prefab", out var prefab6)) campfire = prefab6;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/oilfireballsmall.prefab", out var prefab7)) oilfireballsmall = prefab7;
            if (StringPool.toNumber.TryGetValue("assets/bundled/prefabs/napalm.prefab", out var prefab8)) heli_napalm = prefab8;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/patrol helicopter/rocket_heli_napalm.prefab", out var prefab9)) rocket_heli_napalm = prefab9;
            if (StringPool.toNumber.TryGetValue("assets/prefabs/npc/patrol helicopter/rocket_heli.prefab", out var prefab10)) rocket_heli = prefab10;
        }
#endregion
#region Data
        private void OnNewSave() {
            data = new();
            SaveData();
        }
        private class StoredData {
            public Dictionary<ulong, int> LastSeen = new();
            public DateTime LastRunTime = DateTime.MinValue;
        }
        private StoredData data = new();
        private void LoadData() {
            try {
                data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
            }
            catch (Exception ex) {
                Puts(ex.ToString());
            }
            data ??= new();
            data.LastSeen ??= new();
            if (data.LastRunTime != DateTime.MinValue && DateTime.Now.Subtract(data.LastRunTime).TotalHours >= 24) {
                if (config.AllowKillingSleepersHoursOffline > 0f && data.LastSeen.Count > 0) {
                    Puts("Last seen data wiped due to plugin not being loaded for {0} day(s).", DateTime.Now.Subtract(data.LastRunTime).Days);
                }
                data = new();
                data.LastRunTime = DateTime.Now;
            }
            if (config.AllowKillingSleepersHoursOffline <= 0f) {
                if (data.LastSeen.Count > 0) {
                    data.LastSeen.Clear();
                    SaveData();
                }
                return;
            }
            timer.Every(60f, UpdateLastSeen);
            UpdateLastSeen();
        }
        private void SaveData() {
            data.LastRunTime = DateTime.Now;
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }
        public void UpdateLastSeen() {
            bool changed = false;
            foreach (var sleeper in BasePlayer.sleepingPlayerList) {
                if (sleeper == null || !sleeper.userID.IsSteamId()) {
                    continue;
                }
                if (!data.LastSeen.ContainsKey(sleeper.userID)) {
                    data.LastSeen[sleeper.userID] = Epoch.Current;
                    changed = true;
                }
            }
            foreach (var player in BasePlayer.activePlayerList) {
                if (data.LastSeen.Remove(player.userID)) {
                    changed = true;
                }
            }
            if (changed) {
                SaveData();
            }
        }
        public bool CanKillOfflinePlayer(BasePlayer player, out double timeLeft) {
            timeLeft = 0;
            if (player.IsConnected || !player.IsSleeping()) {
                data.LastSeen.Remove(player.userID);
                return false;
            }
            if (!data.LastSeen.TryGetValue(player.userID, out var lastSeen)) {
                return false;
            }
            double timeOffline = Epoch.Current - lastSeen;
            double allowedOfflineTime = config.AllowKillingSleepersHoursOffline * 3600.0;
            timeLeft = (allowedOfflineTime - timeOffline) / 3600.0;
            return timeOffline > allowedOfflineTime;
        }
#endregion Data
#region Command Handling
        // delegation method for commands
        private void CommandDelegator(IPlayer user, string command, string[] args) {
            // return if user doesn't have access to run console command
            if (!user.IsAdmin) return;
            if (args.Length > 0 && args[0] == "map") {
                if (user.HasPermission(PermCanMap)) {
                    CommandMap(user, command, args);
                }
                return;
            }
            if (args.Contains("pvp")) {
                if (currentRuleSet.rules.Remove("players cannot hurt players")) {
                    currentRuleSet.rules.Add("players can hurt players");
                }
                else if (currentRuleSet.rules.Remove("player can hurt players")) {
                    currentRuleSet.rules.Add("player cannot hurt players");
                }
                Puts("PVP toggled {0}", currentRuleSet.rules.Contains("players can hurt players") ? "on" : "off");
                SaveConfig();
                return;
            }
            if (command == "tpve_prod") {
                HandleProd(user);
                return;
            }
            if (command == "tpve_enable") {
                tpveEnabled = !tpveEnabled;
                ValidateCurrentDamageHook();
                Message(user, "Enable", tpveEnabled);
                return;
            }
            if (command == "tpve" && args.Length != 0) command = args[0];
            else command = command.Replace("tpve.", string.Empty);
            if (!Enum.TryParse(command, out Command @enum)) {
                user.Reply($"Invalid argument: {command}");
                return;
            }
            switch (@enum) {
                case Command.sleepers : HandleSleepers(user);
                return;
                case Command.def : HandleDef(user);
                return;
                case Command.sched : HandleScheduleSet(user, args);
                return;
                case Command.trace : HandleTrace(user);
                return;
                case Command.enable : tpveEnabled = !tpveEnabled;
                ValidateCurrentDamageHook();
                Message(user, "Enable", tpveEnabled);
                return;
                case Command.usage : default : ShowUsage(user);
                return;
            }
        }
        protected void HandleTrace(IPlayer user) {
            if (!IsTraceEnabled(user)) {
                return;
            }
            if (user.IsServer) {
                traceDistance = 0f;
            }
            else traceDistance = config.options.MaxTraceDistance;
            trace = !trace;
            if (!trace) {
                tracePlayer = null;
                traceEntity = null;
            }
            else tracePlayer = user.Object as BasePlayer;
            Message(user, "Notify_TraceToggle", new object[] {
                trace ? "on" : "off"
            }
            );
            traceTimer?.Destroy();
            if (trace) {
                traceTimer = timer.In(traceTimeout, () => trace = false);
            }
        }
        private bool IsTraceEnabled(IPlayer user) {
            if (config.options.PlayerConsole || config.options.ServerConsole) {
                return true;
            }
            Message(user, "`Trace To Player Console` or `Trace To Server Console` must be enabled in the config!");
            return false;
        }
        private void HandleSleepers(IPlayer user) {
            if (animalsIgnoreSleepers) {
                animalsIgnoreSleepers = false;
                if (!IsAnimalsIgnoringSleepers()) {
                    Unsubscribe(nameof(OnNpcTarget));
                }
                user.Reply("Sleepers are no longer protected from animals.");
            }
            else {
                animalsIgnoreSleepers = true;
                Subscribe(nameof(OnNpcTarget));
                user.Reply("Sleepers are now protected from animals.");
            }
        }
        // handle setting defaults
        private void HandleDef(IPlayer user) {
            config.options = new();
            Message(user, "Notify_DefConfigLoad");
            LoadDefaultData();
            Message(user, "Notify_DefDataLoad");
            CheckData();
            SaveConfig();
        }
        // handle prod command (raycast to determine what player is looking at)
        private void HandleProd(IPlayer user) {
            var player = user.Object as BasePlayer;
            if (player == null || !player.IsAdmin) {
                Message(user, "Error_NoPermission");
                return;
            }
            if (!GetRaycastTarget(player, out var entity)) {
                SendReply(player, WrapSize(12, WrapColor("red", GetMessage("Error_NoEntityFound", player.UserIDString))));
                return;
            }
            Message(player, "Notify_ProdResult", entity.GetType(), entity.ShortPrefabName);
        }
        private void CommandMap(IPlayer user, string command, string[] args) {
            // assume args[0] is the command (beyond /tpve)
            if (args.Length > 0) command = args[0];
            // shift arguments
            args = args.Length > 1 ? args[1..] : Array.Empty<string>();
            if (command != "map") {
                Message(user, "Error_InvalidCommand");
            }
            else if (args.Length == 0) {
                Message(user, "Error_InvalidParamForCmd", command);
            }
            else {
                string from = args[0];
                // mapping name
                string to = args.Length == 2 ? args[1] : null;
                // target ruleSet/exclude, otherwise delete mapping
                if (to != null) {
                    if (to != "exclude" && !config.ruleSets.Exists(r => r.name == to)) {
                        // target ruleset must exist, or be "exclude"
                        Message(user, "Error_InvalidMapping", from, to);
                        return;
                    }
                    if (config.HasMapping(from)) {
                        string old = config.mappings[from];
                        Message(user, "Notify_MappingUpdated", from, old, to);
                        // update existing mapping
                    }
                    else Message(user, "Notify_MappingCreated", from, to);
                    // add new mapping
                    config.mappings[from] = to;
                    SaveConfig();
                    TryBuildExclusionMappings();
                }
                else {
                    if (config.HasMapping(from)) {
                        Message(user, "Notify_MappingDeleted", from, config.mappings[from]);
                        config.mappings.Remove(from);
                        // remove mapping
                        SaveConfig();
                        TryBuildExclusionMappings();
                    }
                    else Message(user, "Error_NoMappingToDelete", from);
                }
            }
        }
        // handles schedule enable/disable
        private void HandleScheduleSet(IPlayer user, string[] args) {
            if (args.Length == 0) {
                Message(user, "Error_InvalidParamForCmd");
                return;
            }
            if (!config.schedule.valid) {
                Message(user, "Notify_InvalidSchedule");
            }
            else if (args[0] == "enable") {
                if (config.schedule.enabled) return;
                config.schedule.enabled = true;
                TimerLoop();
                Message(user, "Notify_SchedSetEnabled");
            }
            else if (args[0] == "disable") {
                if (!config.schedule.enabled) return;
                config.schedule.enabled = false;
                if (scheduleUpdateTimer != null) scheduleUpdateTimer.Destroy();
                Message(user, "Notify_SchedSetDisabled");
            }
            else {
                Message(user, "Error_InvalidParameter", args[0]);
            }
        }
#endregion
#region Configuration/Data
        private bool _playersTriggerOption, _playersHurtOption, _canKillOfflinePlayerEnabled, _pvpReflectionEnabled, _allowKillingSleepersEnabled, _blockOutputHandlerEnabled;
        // load config
        protected override void LoadConfig() {
            base.LoadConfig();
            canSaveConfig = false;
            try {
                config = Config.ReadObject<Configuration>();
                if (config == null) LoadDefaultConfig();
                canSaveConfig = true;
                CheckData();
                SaveConfig();
            }
            catch (Exception ex) {
                Puts(ex.ToString());
                LoadDefaultConfig();
            }
        }
        protected override void LoadDefaultConfig() {
            config = new() {
                configVersion = Version.ToString(), options = new()
            }
            ;
            LoadDefaultData();
            Puts("Loaded default config.");
        }
        private bool canSaveConfig = true;
        // save data
        protected override void SaveConfig() {
            if (canSaveConfig) {
                Config.WriteObject(config);
            }
        }
        // check rulesets and groups
        private void CheckData() {
            if (string.IsNullOrEmpty(config.defaultRuleSet)) {
                config.defaultRuleSet = "default";
                Puts("Loaded default ruleset (no default ruleset was configured)");
            }
            if (config.mappings.IsNullOrEmpty()) {
                BuildDefaultMappings();
                Puts("Loaded default mappings (no mappings were configured)");
            }
            if (config.schedule == null) {
                Puts("Loaded default schedule (schedule was null)");
                BuildDefaultSchedule();
            }
            if (config.groups.IsNullOrEmpty()) {
                BuildDefaultGroups();
                Puts("Loaded default entity groups (no entity groups were configured)");
            }
            if (config.ruleSets.IsNullOrEmpty()) {
                BuildDefaultRuleset();
                Puts("Loaded default rulesets (no rulesets were configured)");
            }
            TryUpdateConfig();
            config.configVersion = Version.ToString();
            CheckMappings();
            BuildRuleSetDictionary();
            BuildExclusionMappings();
            _allowKillingSleepersEnabled = config.AllowKillingSleepersAlly || config.AllowKillingSleepers || config.AllowKillingSleepersAuthorization || config.AllowKillingSleepersIds.Exists(x => x.IsSteamId());
            _blockOutputHandlerEnabled = config.options.BlockHandler.Any;
            _pvpReflectionEnabled = config.options.Reflect.Any;
            _canKillOfflinePlayerEnabled = config.AllowKillingSleepersHoursOffline > 0;
            _playersTriggerOption = config.PlayersTriggerTraps || config.PlayersTriggerTurrets;
            _playersHurtOption = config.PlayersHurtTraps || config.PlayersHurtTurrets;
        }
        private void TryUpdateConfig() {
            if (!TryParseVersionNumber(config.configVersion, out var vn) || vn >= Version) return;
            Dictionary<string, string> updates = new(StringComparer.OrdinalIgnoreCase) {
                ["npcs"]
                = "SnakeHazard", ["dispensers"] = "VineSwingingTree"
            }
            ;
            for (int i = 0; i < config.groups.Count; i++) {
                var group = config.groups[i];
                if (string.IsNullOrWhiteSpace(group.members)) {
                    continue;
                }
                if (string.IsNullOrEmpty(group.name)) {
                    group.name = $"group{i}";
                    continue;
                }
                if (group.name == "ridablehorses" && group.members.Equals("RidableHorse2")) {
                    group.members = "RidableHorse";
                    continue;
                }
                if (updates.TryGetValue(group.name, out var update) && !ContainedInGroups(update)) {
                    group.members = $"{group.members.TrimEnd(',', ' ')}{", "}{update}";
                    continue;
                }
            }
        }
        private bool ContainedInGroups(string member) => config.groups.Exists(g => g.members.Contains(member, CompareOptions.OrdinalIgnoreCase) || g.exclusions.Contains(member, CompareOptions.OrdinalIgnoreCase));
        private bool TryParseVersionNumber(string input, out VersionNumber version) {
            version = default;
            if (string.IsNullOrWhiteSpace(input)) return false;
            var parts = input.Split('.');
            if (parts.Length != 3) return false;
            if (int.TryParse(parts[0], out int major) && int.TryParse(parts[1], out int minor) && int.TryParse(parts[2], out int patch)) {
                version = new(major, minor, patch);
                return true;
            }
            return false;
        }
        // rebuild mappings
        private bool CheckMappings() {
            bool dirty = false;
            foreach (RuleSet ruleSet in config.ruleSets) {
                if (!config.mappings.ContainsValue(ruleSet.name)) {
                    config.mappings[ruleSet.name] = ruleSet.name;
                    dirty = true;
                }
            }
            return dirty;
        }
        protected void BuildRuleSetDictionary() {
            ruleSetByNameDictionary.Clear();
            foreach (RuleSet ruleSet in config.ruleSets) {
                if (ruleSet.enabled) {
                    ruleSetByNameDictionary[ruleSet.name] = ruleSet;
                }
            }
        }
        protected void TryBuildExclusionMappings() {
            if (!config.mappings.TryGetValue(AllZones, out var val) || !val.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                BuildExclusionMappings();
            }
        }
        protected void BuildExclusionMappings() {
            excludeAllZones = false;
            exclusionLocationsSet.Clear();
            if (config.mappings.TryGetValue(AllZones, out var val) && val.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                excludeAllZones = true;
                return;
            }
            foreach (var (key, value) in config.mappings) {
                if (!value.Equals("exclude", StringComparison.OrdinalIgnoreCase)) {
                    foreach (var ruleSet in config.ruleSets) {
                        if (ruleSet.name.Equals(value, StringComparison.OrdinalIgnoreCase) && ruleSet.IsEmpty()) {
                            exclusionLocationsSet.Add(key);
                        }
                    }
                }
                else {
                    exclusionLocationsSet.Add(key);
                }
            }
            foreach (var group in config.groups) {
                foreach (var exclusion in group._exclusionSet) {
                    if (!string.IsNullOrWhiteSpace(exclusion)) {
                        exclusionLocationsSet.Add(exclusion.Trim());
                    }
                }
            }
        }
        // load default data to mappings, rulesets, and groups
        protected bool LoadDefaultData() {
            BuildDefaultMappings();
            BuildDefaultSchedule();
            BuildDefaultGroups();
            BuildDefaultRuleset();
            return true;
        }
        protected void BuildDefaultSchedule() {
            config.schedule = new();
        }
        protected void BuildDefaultMappings() {
            config.mappings ??= new();
            config.mappings.Clear();
            config.defaultRuleSet = "default";
            config.mappings[config.defaultRuleSet] = config.defaultRuleSet;
            // create mapping for ruleset
        }
        protected void BuildDefaultGroups() {
            config.groups ??= new();
            config.groups.Clear();
            config.groups.Add(new("barricades") {
                members = "door_barricade_a, door_barricade_a_large, door_barricade_b, door_barricade_dbl_a, door_barricade_dbl_a_large, door_barricade_dbl_b, door_barricade_dbl_b_large, gingerbread_barricades_house, gingerbread_barricades_snowman, gingerbread_barricades_tree, wooden_crate_gingerbread", exclusions = "barricade.concrete, barricade.sandbags, barricade.stone"
            }
            );
            config.groups.Add(new("barricades2") {
                members = "spikes_static, barricade.metal, barricade.wood, barricade.woodwire, spikes.floor, icewall, GraveyardFence",
            }
            );
            config.groups.Add(new("dispensers") {
                members = "BaseCorpse, HelicopterDebris, PlayerCorpse, NPCPlayerCorpse, HorseCorpse, SkyLantern, Pinata"
            }
            );
            config.groups.Add(new("fire") {
                members = "FireBall, FlameExplosive, FlameThrower, BaseOven, FlameTurret, rocket_heli_napalm, napalm, oilfireball2"
            }
            );
            config.groups.Add(new("guards") {
                members = "bandit_guard, scientistpeacekeeper, sentry.scientist.static, sentry.bandit.static"
            }
            );
            config.groups.Add(new("heli") {
                members = "PatrolHelicopter, oilfireballsmall, heli_napalm, rocket_heli, rocket_heli_napalm"
            }
            );
            config.groups.Add(new("highwalls") {
                members = "SimpleBuildingBlock, wall.external.high.ice, gates.external.high.stone, gates.external.high.wood"
            }
            );
            config.groups.Add(new("ridablehorses") {
                members = "RidableHorse"
            }
            );
            config.groups.Add(new("cars") {
                members = "BasicCar, ModularCar, BaseModularVehicle, BaseVehicleModule, VehicleModuleEngine, VehicleModuleSeating, VehicleModuleStorage, VehicleModuleTaxi, ModularCarSeat, Bike"
            }
            );
            config.groups.Add(new("mini") {
                members = "minicopter.entity"
            }
            );
            config.groups.Add(new("scrapheli") {
                members = "ScrapTransportHelicopter"
            }
            );
            config.groups.Add(new("ch47") {
                members = "ch47.entity"
            }
            );
            config.groups.Add(new("npcs") {
                members = "ch47scientists.entity, BradleyAPC, CustomScientistNpc, SnakeHazard, ScarecrowNPC, HumanNPC, NPCPlayer, ScientistNPC, TunnelDweller, SimpleShark, UnderwaterDweller, ZombieNPC"
            }
            );
            config.groups.Add(new("players") {
                members = "BasePlayer, FrankensteinPet"
            }
            );
            config.groups.Add(new("resources") {
                members = "ResourceEntity, TreeEntity, OreResourceEntity, LootContainer, NaturalBeehive, VineSwingingTree", exclusions = "hobobarrel.deployed"
            }
            );
            config.groups.Add(new("snowmobiles") {
                members = "snowmobile, tomahasnowmobile"
            }
            );
            config.groups.Add(new("traps") {
                members = "AutoTurret, BearTrap, FlameTurret, Landmine, GunTrap, ReactiveTarget, TeslaCoil, spikes.floor"
            }
            );
            config.groups.Add(new("junkyard") {
                members = "magnetcrane.entity, carshredder.entity"
            }
            );
            config.groups.Add(new("tugboats") {
                members = "Tugboat"
            }
            );
            config.groups.Add(new("heliturrets") {
                members = "turret_attackheli"
            }
            );
            config.groups.Add(new("ramhead") {
                members = "BatteringRamHead"
            }
            );
            config.groups.Add(new("siege") {
                members = "SiegeTower, Catapult, Ballista, BallistaGun, BatteringRam, ConstructableEntity"
            }
            );
            config.groups.Add(new("bees") {
                members = "BeeSwarmAI, Beehive, BeeGrenade, BeeSwarmMaster, NaturalBeehive"
            }
            );
            config.groups.Add(new("farm") {
                members = "simplechicken.entity, FarmableAnimal, ChickenCoop"
            }
            );
        }
        protected void BuildDefaultRuleset() {
            config.ruleSets ??= new();
            config.ruleSets.Clear();
            // create default ruleset
            RuleSet defaultRuleSet = new(config.defaultRuleSet) {
                _flags = RuleFlags.HopperCannotTargetEnemyLoot | RuleFlags.AuthorizedFarmableDamage | RuleFlags.HumanNPCDamage | RuleFlags.LockedBoxesImmortal | RuleFlags.LockedDoorsImmortal | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.TrapsIgnorePlayers | RuleFlags.TurretsIgnorePlayers, flags = "HopperCannotTargetEnemyLoot, AuthorizedFarmableDamage, HumanNPCDamage, LockedBoxesImmortal, LockedDoorsImmortal, PlayerSamSitesIgnorePlayers, TrapsIgnorePlayers, TurretsIgnorePlayers"
            }
            ;
            // create rules and add to ruleset
            defaultRuleSet.AddRule(this, "anything can hurt dispensers");
            defaultRuleSet.AddRule(this, "anything can hurt resources");
            defaultRuleSet.AddRule(this, "anything can hurt barricades");
            defaultRuleSet.AddRule(this, "anything can hurt traps");
            defaultRuleSet.AddRule(this, "anything can hurt heli");
            defaultRuleSet.AddRule(this, "anything can hurt npcs");
            defaultRuleSet.AddRule(this, "anything can hurt players");
            defaultRuleSet.AddRule(this, "nothing can hurt ch47");
            defaultRuleSet.AddRule(this, "nothing can hurt cars");
            defaultRuleSet.AddRule(this, "nothing can hurt mini");
            defaultRuleSet.AddRule(this, "nothing can hurt snowmobiles");
            defaultRuleSet.AddRule(this, "nothing can hurt ridablehorses");
            defaultRuleSet.AddRule(this, "cars cannot hurt anything");
            defaultRuleSet.AddRule(this, "mini cannot hurt anything");
            defaultRuleSet.AddRule(this, "ch47 cannot hurt anything");
            defaultRuleSet.AddRule(this, "scrapheli cannot hurt anything");
            defaultRuleSet.AddRule(this, "players cannot hurt players");
            defaultRuleSet.AddRule(this, "players cannot hurt traps");
            defaultRuleSet.AddRule(this, "guards cannot hurt players");
            defaultRuleSet.AddRule(this, "fire cannot hurt players");
            defaultRuleSet.AddRule(this, "traps cannot hurt players");
            defaultRuleSet.AddRule(this, "highwalls cannot hurt players");
            defaultRuleSet.AddRule(this, "barricades2 cannot hurt players");
            defaultRuleSet.AddRule(this, "mini cannot hurt mini");
            defaultRuleSet.AddRule(this, "npcs can hurt players");
            defaultRuleSet.AddRule(this, "junkyard cannot hurt anything");
            defaultRuleSet.AddRule(this, "junkyard can hurt cars");
            defaultRuleSet.AddRule(this, "players cannot hurt tugboats");
            defaultRuleSet.AddRule(this, "heliturrets cannot hurt players");
            defaultRuleSet.AddRule(this, "ramhead can hurt ramhead");
            defaultRuleSet.AddRule(this, "siege cannot hurt players");
            defaultRuleSet.AddRule(this, "players cannot hurt farm");
            config.ruleSets.Add(defaultRuleSet);
            // add ruleset to rulesets list
        }
        private bool ResetRules(string key) {
            if (string.IsNullOrEmpty(key) || config == null) {
                return false;
            }
            string old = config.defaultRuleSet;
            config.defaultRuleSet = key;
            currentRuleSet = config.GetDefaultRuleSet();
            if (currentRuleSet == null) {
                config.defaultRuleSet = old;
                currentRuleSet = config.GetDefaultRuleSet();
            }
            ValidateCurrentDamageHook();
            return currentRuleSet != null;
        }
#endregion
#region Trace
        private StringBuilder _tsb = new();
        private BaseEntity traceEntity;
        private BasePlayer tracePlayer;
        private float traceDistance;
        private void Trace(string message, int indentation = 0) {
            if (traceEntity == null || traceEntity.IsDestroyed) {
                return;
            }
            bool playerInRange = tracePlayer != null && !tracePlayer.IsDestroyed && InRange(tracePlayer.transform.position, traceEntity.transform.position, traceDistance);
            bool shouldLogToConsole = (config.options.PlayerConsole && playerInRange) || (config.options.ServerConsole && (traceDistance == 0 || playerInRange));
            if (shouldLogToConsole) {
                _tsb.Append(new string(' ', indentation)).AppendLine(message);
            }
        }
        private void LogTrace() {
            var text = _tsb.ToString();
            traceEntity = null;
            _tsb.Length = 0;
            try {
                if (!string.IsNullOrEmpty(text)) {
                    if (config.options.ServerConsole) {
                        Puts(text);
                    }
                    if (config.options.PlayerConsole && tracePlayer != null && tracePlayer.IsConnected) {
                        tracePlayer.ConsoleMessage(text);
                    }
                    if (config.options.LogToFile) {
                        LogToFile(traceFile, text, this);
                    }
                }
            }
            catch (IOException) {
                timer.Once(1f, () => LogToFile(traceFile, text, this));
            }
        }
#endregion Trace
#region Hooks/Handler Procedures
        private void OnPlayerConnected(BasePlayer player) {
            if (config.schedule.broadcast && !string.IsNullOrEmpty(currentBroadcastMessage)) {
                SendReply(player, GetMessage("Prefix") + currentBroadcastMessage);
            }
        }
        private string CurrentRuleSetName() => currentRuleSet?.name;
        private bool IsEnabled() => tpveEnabled;
        private void OnTimedExplosiveExplode(TimedExplosive explosive, Vector3 explosionFxPos) {
            if (explosive != null) {
                explosive.splashWallpaperThroughWalls = false;
            }
        }
        private object OnWallpaperRemove(BuildingBlock block, int side) {
            if (block == null || block.IsDestroyed) {
                return null;
            }
            switch (side) {
                case 0 : {
                    if (block.wallpaperID != 0 && block.wallpaperHealth <= 0f) {
                        using var entityLocations = GetLocationKeys(block);
                        if (CheckExclusion(entityLocations, entityLocations, trace)) {
                            return null;
                        }
                        if (trace) Trace("Block Damage Wallpaper1 enabled; block and return", 1);
                        block.wallpaperHealth = block.health;
                        return true;
                    }
                    break;
                }
                case 1 : {
                    if (block.wallpaperID2 != 0 && block.wallpaperHealth2 <= 0f) {
                        using var entityLocations = GetLocationKeys(block);
                        if (CheckExclusion(entityLocations, entityLocations, trace)) {
                            return null;
                        }
                        if (trace) Trace("Block Damage Wallpaper2 enabled; block and return", 1);
                        block.wallpaperHealth2 = block.health;
                        return true;
                    }
                    break;
                }
            }
            return null;
        }
        private object OnEntityTakeDamage(ResourceEntity entity, HitInfo info) {
            if (info == null || info.Initiator == null) {
                return null;
            }
            RuleSet ruleSet;
            if (useZones) {
                // get entity and initiator locations (zones)
                using var entityLocations = GetLocationKeys(entity);
                using var initiatorLocations = GetLocationKeys(info.Initiator);
                // check for exclusion zones (zones with no rules mapped)
                if (CheckExclusion(entityLocations, initiatorLocations, trace)) {
                    if (trace) Trace("Exclusion found; allow and return", 1);
                    return null;
                }
                if (trace) Trace("No exclusion found - looking up RuleSet...", 1
// ... [Code preview truncated due to length. Full source in attached file] ...

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