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

FREE BrokenItemsCleaner : 1.3.1 1.3.1

Нет прав для скачивания
C#:
//#define TESTING
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("BrokenItemsCleaner", "EcoSmile/Vlad-00003", "1.3.1")]
    public class BrokenItemsCleaner : RustPlugin
    {
        private Coroutine _cleanup;
        private CleanupData _cleanupData;

        private void Loaded()
        {
            _cleanupData = new CleanupData();
#if TESTING
            StopwatchWrapper.OnComplete = DebugMessage;
#endif
        }

        private void OnServerSave()
        {
            if (_cleanup != null || SaveRestore.IsSaving)
            {
                ServerMgr.Instance.Invoke(OnServerSave, 5f);
                return;
            }

            _cleanup = ServerMgr.Instance.StartCoroutine(PreformCleanup());
        }

        private void Unload()
        {
            if (ServerMgr.Instance == null)
                return;
            ServerMgr.Instance.CancelInvoke(OnServerSave);

            if (_cleanup == null)
                return;
            ServerMgr.Instance.StopCoroutine(_cleanup);
            _cleanupData.Clear();
        }


        #region CleanupData

        private class CleanupData
        {
            private readonly Stopwatch _stopwatch = new Stopwatch();
            public readonly HashSet<ulong> HeldEntities = new HashSet<ulong>();
            public readonly Dictionary<uint, Item[]> Items = new Dictionary<uint, Item[]>();
            public int Removed;
            public int Repaired;

            public void Add(IItemGetter selector, BaseNetworkable[] array)
            {
                foreach (var entity in selector.Entities(array))
                {
                    var items = selector.GetItems(entity);
                    if (items != null)
                        Items[entity.net.ID] = items.ToArray();
                }
            }

            public bool InvalidOrListed(HeldEntity entity)
            {
                return !entity || !entity.IsValid() || HeldEntities.Contains(entity.net.ID);
            }

            public void Clear()
            {
                HeldEntities.Clear();
                Items.Clear();
                Repaired = 0;
                Removed = 0;
            }

            public void StartNew()
            {
                _stopwatch.Restart();
            }

            public void Stop()
            {
                _stopwatch.Stop();
            }

            public override string ToString()
            {
                return $"Предметов удалено/Entity Removed: {Removed}\n" +
                       $"Предметов восстановлено/Entity Repaired: {Repaired}\n" +
                       $"Очистка заняла/Cleanup took:{_stopwatch.ElapsedMilliseconds}\n";
            }
        }

        #endregion

        #region Cleanup

        #region ItemGetter

        private interface IItemGetter
        {
            string TypeName { get; }
            IEnumerable<Item> GetItems(object obj);
            IEnumerable<BaseNetworkable> Entities(BaseNetworkable[] array);
        }

        private class ItemGetter<T> : IItemGetter where T : BaseNetworkable
        {
            private readonly Func<T, IEnumerable<Item>> _getter;

            public ItemGetter(Func<T, IEnumerable<Item>> getter)
            {
                _getter = getter;
                TypeName = typeof(T).FullName;
            }

            public IEnumerable<Item> GetItems(object obj)
            {
                return _getter(obj as T);
            }

            public IEnumerable<BaseNetworkable> Entities(BaseNetworkable[] array)
            {
                return array.OfType<T>();
            }

            public string TypeName { get; }
        }

        #endregion


        private static readonly List<IItemGetter> ItemGetters = new List<IItemGetter>
        {
            new ItemGetter<StorageContainer>(x => x.inventory?.itemList),
            new ItemGetter<LootableCorpse>(x => x.containers?.SelectMany(y => y?.itemList)),
            new ItemGetter<DroppedItemContainer>(x => x.inventory?.itemList),
            new ItemGetter<ContainerIOEntity>(x => x.inventory?.itemList),
            new ItemGetter<BaseRidableAnimal>(x => x.inventory?.itemList),
            new ItemGetter<DroppedItem>(x => new[] {x.item}),
            new ItemGetter<BasePlayer>(x => x.inventory?.AllItems())
        };


        private void CheckLost(IEnumerable<Item> items)
        {
            foreach (var item in items)
            {
                if (item?.IsValid() != true)
                    continue;

                var held = item.GetHeldEntity() as HeldEntity;
                if (held?.IsValid() == true)
                {
                    _cleanupData.HeldEntities.Add(held.net.ID);
                    continue;
                }

                item.OnItemCreated();
                item.MarkDirty();
                held = item.GetHeldEntity() as HeldEntity;
                if (held?.IsValid() == true)
                {
                    _cleanupData.HeldEntities.Add(held.net.ID);
                    _cleanupData.Repaired++;
                }
            }
        }

        private IEnumerator PreformCleanup()
        {
            yield return CoroutineEx.waitForSeconds(2);

            PrintWarning("Очистка запущена/Cleaning started");

            var array = BaseNetworkable.serverEntities.ToArray();
            yield return null;

            _cleanupData.StartNew();

            foreach (var itemGetter in ItemGetters)
            {
#if TESTING
                using (new StopwatchWrapper($"Lookup for items in entities of type {itemGetter.TypeName} took {{0}}ms."))
#endif
                {
                    _cleanupData.Add(itemGetter, array);
                }
            }

            yield return CoroutineEx.waitForEndOfFrame;

#if TESTING
            using (new StopwatchWrapper("Checking all items took {0}ms."))
#endif
            {
                foreach (var item in _cleanupData.Items)
                {
                    CheckLost(item.Value);
                    yield return CoroutineEx.waitForEndOfFrame;
                }
            }

#if TESTING
            var text = "";
            using (new StopwatchWrapper("Clearing leftover HeldEntities took {0}ms."))
#endif
            {
                foreach (var entity in array.OfType<HeldEntity>())
                {
                    if (_cleanupData.InvalidOrListed(entity))
                        continue;
                    var parentEntity = entity.GetParentEntity();
#if TESTING
                    text +=
                        $"Removing entity {entity}, parent: {(parentEntity == null ? "null" : parentEntity.ToString())}, ";
#endif
                    entity.Kill();
#if TESTING
                    text += $"removed? {entity.IsDestroyed}\n";
#endif
                    _cleanupData.Removed++;
                    yield return null;
                }
            }
#if TESTING
            LogToFile("DeletedItems", text, this);
#endif
            _cleanupData.Stop();
            yield return null;

            PrintWarning($"Очистка завершена/Cleanup Completed:\n{_cleanupData}");
            _cleanupData.Clear();
            _cleanup = null;
        }

        #endregion

        #region Testing functions

#if TESTING
        private void DebugMessage(string format, long time)
        {
            PrintWarning(format, time);
        }

        private class StopwatchWrapper : IDisposable
        {
            public StopwatchWrapper(string format)
            {
                Sw = new Stopwatch();
                Sw.Start();
                Format = format;
            }

            public static Action<string, long> OnComplete { private get; set; }

            private string Format { get; }
            private Stopwatch Sw { get; }

            public long Time { get; private set; }

            public void Dispose()
            {
                Sw.Stop();
                Time = Sw.ElapsedMilliseconds;
                OnComplete(Format, Time);
            }
        }

#endif

        #endregion
    }
}

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