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

FREE DecaySystem : 1.1.0 1.1.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Rust;
using UnityEngine;
using System.Diagnostics;
namespace Oxide.Plugins {
    [Info("DecaySystem", "bazuka5801", "1.1.0")]
    class DecaySystem : RustPlugin {
#region CLASSES
        class Vector3 {
            public float x, y, z;
            public Vector3(string s) {
                var ss = s.Split(' ');
                this.x = float.Parse(ss[0]);
                this.y = float.Parse(ss[1]);
                this.z = float.Parse(ss[2]);
            }
            public string Save() => $"{x} {y} {z}";
            public Vector3(float x, float y, float z) {
                this.x = x;
                this.y = y;
                this.z = z;
            }
            public Vector3(UnityEngine.Vector3 vec) {
                this.x = vec.x;
                this.y = vec.y;
                this.z = vec.z;
            }
            public UnityEngine.Vector3 ToVector3() => new UnityEngine.Vector3(x, y, z);
            public static bool Equal(Vector3 a, Vector3 b) => UnityEngine.Vector3.Distance(a.ToVector3(), b.ToVector3()) < 0.001;
            public static bool Equal(UnityEngine.Vector3 a, UnityEngine.Vector3 b) => UnityEngine.Vector3.Distance(a, b) < 0.001;
        }
#endregion
#region PLUGINS API
        [PluginReference]
        Plugin ZoneManager;
        bool inZone(UnityEngine.Vector3 vec) {
            if (ZoneManager == null) return false;
            return (bool)ZoneManager.Call("inZone", vec);
        }
#endregion
#region CONST VARIABLES
        private List<string> baseDecayPrefabs = new List<string>() {
            "foundation", "foundation.triangle", "gates.external.high.wood", "wall.external.high.wood", "gates.external.high.stone", "wall.external.high.stone",
        }
        ;
        private List<string> defaultDecayPrefabs = new List<string>() {
            "foundation", "foundation.triangle", "wall.external.high.stone", "cupboard.tool.deployed", "box.wooden.large", "sleepingbag_leather_deployed", "stocking_large_deployed", "stocking_small_deployed", "furnace", "woodbox_deployed", "barricade.sandbags", "barricade.stone", "jackolantern.happy", "barricade.concrete", "floor.grill", "barricade.metal", "autoturret_deployed", "campfire", "repairbench_deployed", "beartrap", "wall.external.high.wood", "bed_deployed", "gates.external.high.stone", "furnace.large", "refinery_small_deployed", "reactivetarget_deployed", "barricade.woodwire", "landmine", "lantern.deployed", "ceilinglight.deployed", "gates.external.high.wood", "spikes.floor", "barricade.wood", "jackolantern.angry", "water_catcher_large"
        }
        ;
#endregion
        List<BaseCombatEntity> decayEntities;
        Dictionary<uint, int> doorTimers = new Dictionary<uint, int>();
        Dictionary<uint, uint> blocksDoor = new Dictionary<uint, uint>();
        int WorldBuildingsLayer = LayerMask.GetMask("Construction", "World", "Terrain");
        int BuildingsLayer = LayerMask.GetMask("Construction");
#region CONGIGURATION
        private int doorTimeout;
        private float doorRadius;
        private float timeout = 3600;
        private Dictionary<string, int> decaySettings = new Dictionary<string, int>();
        protected override void LoadDefaultConfig() {
            Config["Задержка после открытия/закрытия двери"] = doorTimeout = GetConfig("Задержка после открытия/закрытия двери", 5);
            Config["Радиус обнаружения дверей"] = doorRadius = GetConfig("Радиус обнаружения дверей", 10f);
            Config["Гниение объектов"] = decaySettings = GetConfig("Гниение объектов", defaultDecayPrefabs.ToDictionary(p => p, p => (object)2)).ToDictionary(p => p.Key, p => int.Parse(p.Value.ToString()));
            SaveConfig();
        }
        T GetConfig<T>(string name, T defaultValue) => Config[name] == null ? defaultValue : (T)Convert.ChangeType(Config[name], typeof(T));
#endregion
#region DATA
        private DynamicConfigFile doortimers_file = Interface.Oxide.DataFileSystem.GetFile("DecaySystem_DoorTimers");
        private DynamicConfigFile blocksDoorFile = Interface.Oxide.DataFileSystem.GetFile("DecaySystem_Blocks");
        void LoadDoorTimers() {
            doorTimers = doortimers_file.ReadObject<Dictionary<string, int>>().ToDictionary(v => uint.Parse(v.Key), t => t.Value);
            blocksDoor = blocksDoorFile.ReadObject<Dictionary<string, uint>>().ToDictionary(k => Convert.ToUInt32(k.Key), v => v.Value);
        }
        void Unload() {
            OnServerSave();
            if (decayCoroutine != null) ServerMgr.Instance.StopCoroutine(decayCoroutine);
        }
        void OnServerSave() {
            Dictionary<string, int> serializedTimers = new Dictionary<string, int>();
            foreach (var d in doorTimers) {
                if (!serializedTimers.ContainsKey(d.Key.ToString())) serializedTimers.Add(d.Key.ToString(), d.Value);
            }
            doortimers_file.WriteObject(serializedTimers);
            blocksDoorFile.WriteObject(blocksDoor.ToDictionary(k => k.Key.ToString(), v => v.Value));
        }
#endregion
#region COMMANDS
        [ConsoleCommand("repairallentities")]
        void cmdRepairAllEntities(ConsoleSystem.Arg arg) {
            if (arg.Connection != null) return;
            var objs = UnityEngine.Object.FindObjectsOfType<BaseCombatEntity>().Where(IsDecayEntity).ToList();
            foreach(var ent in objs) {
                if (ent != null) ent.Heal(ent.MaxHealth()-ent.health);
            }
            Puts($"Repaired {objs.Count} entities!");
        }
        [ChatCommand("decay")]
        void cmdChatDecay(BasePlayer player) {
            if (!player.IsAdmin) return;
            RunDecay();
        }
        [ConsoleCommand("decay")]
        void cmdDecay(ConsoleSystem.Arg arg) {
            if (arg.Connection != null) return;
            RunDecay();
        }
        /*[ConsoleCommand("setdoors")]
        void cmdSetDoors(ConsoleSystem.Arg arg)
        {
            if (arg.Connection != null) return;
            foreach (var door in UnityEngine.Object.FindObjectsOfType<Door>())
            {
                if (door?.net?.ID != null)
                    doorTimers[door.net.ID] = doorTimeout;
            }
            Puts("success");
        }*/
#endregion
#region OXIDE HOOKS
        private bool init = false;
        private bool isdecay = false;
        private UnityEngine.Vector3 offset = new UnityEngine.Vector3(0, 0.5f, 0);
        private Coroutine decayCoroutine;
        void OnServerInitialized() {
            LoadDefaultConfig();
            LoadDoorTimers();
            timer.Every(timeout, RunDecay);
            InitDecayEntities();
        }
        void InitDecayEntities() {
            var ents = UnityEngine.Object.FindObjectsOfType<BaseCombatEntity>();
            decayEntities = new List<BaseCombatEntity>(ents.Length);
            Parallel.For(0, ents.Length, (i)=> {
                var ent = ents[i];
                if (ent == null) return;
                DecayEntity decEnt = ent as DecayEntity;
                if (decEnt != null) {
                    decEnt.CancelInvoke("RunDecay");
                }
                if (IsDecayEntity(ent)) decayEntities.Add(ent);
            }
            );
            Puts($"Загрузка объектов прошла успешно! Count: {ents.Length}");
            init = true;
        }
        void OnEntitySpawned(BaseNetworkable entity) {
            if (!init) return;
            if (entity?.net?.ID == null) return;
            if (IsDecayEntity(entity) && !decayEntities.Contains((BaseCombatEntity)entity)) decayEntities.Add((BaseCombatEntity)entity);
            if (entity is Door) doorTimers[entity.net.ID] = doorTimeout;
            NextTick(() => {
                DecayEntity decEnt = entity as DecayEntity;
                if (decEnt != null) {
                    decEnt.CancelInvoke("RunDecay");
                }
            }
            );
        }
        bool IsDecayEntity(BaseNetworkable entity) {
            if (decaySettings.ContainsKey(entity.ShortPrefabName)) {
                if (!baseDecayPrefabs.Contains(entity.ShortPrefabName)) {
                    RaycastHit hit;
                    var ray = new Ray(entity.transform.position + offset, entity.transform.TransformDirection(UnityEngine.Vector3.down));
                    if (!Physics.Raycast(ray, out hit, 5, WorldBuildingsLayer, QueryTriggerInteraction.Ignore)) return true;
                    if (hit.transform.gameObject.layer == BuildingsLayer) return false;
                }
                return true;
            }
            return false;
        }
        void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
            if (!init) return;
            if (entity?.net?.ID == null) return;
            if (entity is Door) doorTimers.Remove(entity.net.ID);
            if (decaySettings.ContainsKey(entity.ShortPrefabName)) decayEntities.Remove(entity);
        }
        void OnDoorOpened(Door door, BasePlayer player) {
            DoorHandle(door);
        }
        void OnDoorClosed(Door door, BasePlayer player) {
            DoorHandle(door);
        }
#endregion
#region CORE
        void DoorHandle(Door door) {
            doorTimers[door.net.ID] = doorTimeout;
        }
        int GetDoorTimeout(BaseCombatEntity block) {
            uint blockID = block.net.ID;
            uint doorNetID;
            if (blocksDoor.TryGetValue(blockID, out doorNetID)) {
                int doorDecayCooldown;
                if (doorTimers.TryGetValue(doorNetID, out doorDecayCooldown) && doorDecayCooldown > 0) return doorDecayCooldown;
                blocksDoor.Remove(blockID);
            }
            var position = block.GetNetworkPosition();
            List<BaseEntity> nearby = new List<BaseEntity>();
            Vis.Entities(position, doorRadius, nearby, BuildingsLayer, QueryTriggerInteraction.Ignore);
            int timeout = -1;
            foreach (var entity in nearby) {
                var door = entity as Door;
                if (door != null) {
                    if (!doorTimers.TryGetValue(door.net.ID, out timeout) || timeout <= 0) continue;
                    blocksDoor[blockID] = door.net.ID;
                    break;
                }
            }
            return timeout;
        }
        void RunDecay() {
            if (decayCoroutine != null) ServerMgr.Instance.StopCoroutine(decayCoroutine);
            decayCoroutine = ServerMgr.Instance.StartCoroutine(Decay());
        }
        IEnumerator Decay() {
            isdecay = true;
            PrintToChat("<size=18><color=#fee3b4>Запущена оптимизация карты</color></size>\nПожалуйста, ожидайте...");
            List<uint> remove = doorTimers.Keys.ToList().Where(door => --doorTimers[door] < 0).ToList();
            foreach (var d in remove) doorTimers.Remove(d);
            int i = 0;
            int count = decayEntities.Count;
            int die = 0;
            int lastpercent = -1;
            var start = DateTime.UtcNow;
            decayEntities.RemoveAll(item => item == null || item.IsDestroyed);
            StopwatchStart("DecaySystem");
            foreach (var block in decayEntities.ToArray()) {
                i++;
                var percent = (int)(i / (float)count * 100);
                if (StopwatchElapsedMilliseconds("DecaySystem") > 10 || percent != lastpercent) {
                    StopwatchStart("DecaySystem");
                    if (percent != lastpercent) {
                        if (percent % 20 == 0) Puts($"Идёт оптимизация карты: {percent}%");
                        lastpercent = percent;
                        yield return new WaitForSeconds(0.2f);
                    }
                }
                if (Performance.report.frameRate < 150 || Performance.current.frameRate < 150) yield return new WaitForEndOfFrame();
                if (block == null) continue;
                if (block.IsDestroyed) continue;
                if (block.transform == null) continue;
                if (inZone(block.transform.position)) continue;
                var t = GetDoorTimeout(block);
                if (t <= 0) {
                    block.Hurt(block.MaxHealth() / decaySettings[block.ShortPrefabName], DamageType.Decay);
                    if (block.IsDead()) {
                        die++;
                        yield return new WaitForEndOfFrame();
                    }
                }
            }
            var time = DateTime.UtcNow.Subtract(start).TotalSeconds.ToString("F2");
            Puts($"count:{count} die:{die}");
            PrintToChat($"<size=18><color=#fee3b4>Оптимизация карты завершена</color></size>\nОбработанно объектов: <color=#fee3b4>{count}</color>\nРазрушенно объектов: <color=#fee3b4>{die}</color>\nЗатрачено времени: <color=#fee3b4>{time}c</color>");
            List<BaseCombatEntity> list = new List<BaseCombatEntity>(decayEntities.Count);
            i = 0;
            foreach (var p in decayEntities.ToArray()) {
                if (p != null) list.Add(p);
                if (i++ % 100 == 0) yield return new WaitForFixedUpdate();
            }
            decayEntities = list;
            isdecay = false;
        }
        static Dictionary<string, Stopwatch> watches = new Dictionary<string, Stopwatch>();
        /// <summary>
        /// Start Stopwatch
        /// </summary>
        /// <param name="name">KEY</param>
        public static void StopwatchStart(string name) {
            watches[name] = Stopwatch.StartNew();
        }
        /// <summary>
        /// Get Elapsed Milliseconds
        /// </summary>
        /// <param name="name">KEY</param>
        /// <returns></returns>
        public static long StopwatchElapsedMilliseconds(string name) => watches[name].ElapsedMilliseconds;
        /// <summary>
        /// Remove StopWatch
        /// </summary>
        /// <param name="name"></param>
        public static void StopwatchStop(string name) {
            watches.Remove(name);
        }
#endregion
    }
}

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