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

FREE ImageLibrary : 1.0.4 1.0.4

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//Reference: Facepunch.Sqlite
//Reference: UnityEngine.UnityWebRequestModule
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;

namespace Oxide.Plugins
{	
	/* Based on version 2.0.45 by Absolut & K1lly0u */
	
	// продумать калбеки, что бы всегда вызывались ? Сейчас они не всегда будут вызыватся, например если картинка уже есть или в случаях ошибок	
	// продумать кеш для клиента, добавить ли апи инициализации картинок на клиенте ?
	
	// !!! В ПЛАГИНЕ ИСПОЛЬЗУЕТСЯ МОЙ STEAM API KEY.ПЛАГИН НЕ ПЕРЕДАВАТЬ НИКОМУ !!!
	
	/*
		Правило пользования плагином:
		 - добавить нужные свои картинки заранее на OnServerInitialization, желательно сделав либо задержку (что бы ImageLibrary успел инициализироватся), 
		   либо в цикле ждать IsReady и только после этого добавлять картинки
		 - на те скины которые нужны в работе (кроме стандартных) следует заранее сделать на них GetImage, что бы они прогрузились в кеш сервера
		 - в своем плагине не стоит использовать дополнительное кеширование возвращаемых данных GetImage, это будет вредно
	*/
	
    [Info("Image Library", "Nimant", "1.0.4")]    
    class ImageLibrary : RustPlugin
    {
        #region Fields        

        private static ImageLibrary instance;
		private static uint instanceID;
        private static ImageAssets assets;
        
		private static string SteamPublishedSkinsURL = "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/";
		
        private static bool isInitialized;
		
		private static System.Random Rnd = new System.Random();
		
		private static bool NeedStandartSave, NeedWorkshopSkinsSave, NeedPlayerAvatarsSave, NeedCustomImagesSave;

		private static List<string> ItemShortNames = new List<string>();
		
		private static Dictionary<ulong, WorkshopSkinInfo> WorkShopAllowItems = new Dictionary<ulong, WorkshopSkinInfo>();				
		
        private static readonly Regex avatarFilter = new Regex(@"<avatarFull><!\[CDATA\[(.*)\]\]></avatarFull>");
		
		private static readonly Dictionary<string, string> workshopNameToShortname = new Dictionary<string, string>
        {
            {"ak47", "rifle.ak" },
            {"balaclava", "mask.balaclava" },
            {"bandana", "mask.bandana" },
            {"bearrug", "rug.bear" },
            {"beenie", "hat.beenie" },
            {"boltrifle", "rifle.bolt" },
            {"boonie", "hat.boonie" },
            {"buckethat", "bucket.helmet" },
            {"burlapgloves", "burlap.gloves" },
            {"burlappants", "burlap.trousers" },
            {"cap", "hat.cap" },
            {"collaredshirt", "shirt.collared" },
            {"deerskullmask", "deer.skull.mask" },
            {"hideshirt", "attire.hide.vest" },
            {"hideshoes", "attire.hide.boots" },
            {"longtshirt", "tshirt.long" },
            {"lr300", "rifle.lr300" },
            {"minerhat", "hat.miner" },
            {"mp5", "smg.mp5" },
            {"pipeshotgun", "shotgun.waterpipe" },
            {"roadsignpants", "roadsign.kilt" },
            {"roadsignvest", "roadsign.jacket" },
            {"semiautopistol", "pistol.semiauto" },
            {"snowjacket", "jacket.snow" },
            {"sword", "salvaged.sword" },
            {"vagabondjacket", "jacket" },
            {"woodstorage", "box.wooden" },
            {"workboots", "shoes.boots" }
        };
		
		private class SteamSkinPublishDet
        {
            [JsonProperty("response")] 
			public Response response;

            public class Tag
            {
                [JsonProperty("tag")] 
				public string tag;
            }

            public class Response
            {
                [JsonProperty("result")] 
				public int result;
                [JsonProperty("resultcount")] 
				public int resultcount;
                [JsonProperty("publishedfiledetails")] 
				public List<Publishedfiledetail> publishedfiledetails;

                public class Publishedfiledetail
                {
                    [JsonProperty("publishedfileid")] 
					public ulong publishedfileid;
                    [JsonProperty("result")] 
					public int result;
                    [JsonProperty("creator")] 
					public string creator;
                    [JsonProperty("creator_app_id")] 
					public int creator_app_id;
                    [JsonProperty("consumer_app_id")] 
					public int consumer_app_id;
                    [JsonProperty("filename")] 
					public string filename;
                    [JsonProperty("file_size")] 
					public int file_size;
                    [JsonProperty("preview_url")] 
					public string preview_url;
                    [JsonProperty("hcontent_preview")] 
					public string hcontent_preview;
                    [JsonProperty("title")] 
					public string title;
                    [JsonProperty("description")] 
					public string description;
                    [JsonProperty("time_created")] 
					public int time_created;
                    [JsonProperty("time_updated")] 
					public int time_updated;
                    [JsonProperty("visibility")] 
					public int visibility;
                    [JsonProperty("banned")] 
					public int banned;
                    [JsonProperty("ban_reason")] 
					public string ban_reason;
                    [JsonProperty("subscriptions")] 
					public int subscriptions;
                    [JsonProperty("favorited")] 
					public int favorited;

                    [JsonProperty("lifetime_subscriptions")]
                    public int lifetime_subscriptions;

                    [JsonProperty("lifetime_favorited")] 
					public int lifetime_favorited;
                    [JsonProperty("views")] 
					public int views;
                    [JsonProperty("tags")] 
					public List<Tag> tags;
                }
            }
        }

        #endregion Fields
		
		#region Hooks
		
		private void Init() 
		{
			LoadVariables();
			LoadData();
			
			if (string.IsNullOrEmpty(configData.SteamApiKey))
				configData.SteamApiKey = "4069ACBB1052EDB8D40A24B448A5C397";
			
			SaveConfig(configData);
		}
		
		private void OnServerInitialized()
        {
			instance = this;
			isInitialized = false;
			
			instanceID = 0;
			try { instanceID = CommunityEntity.ServerInstance.net.ID; } catch { }			
			if (instanceID == 0)
			{
				PrintError("Плагин не инициализирован, ServerInstance ID пустой!");
				return;
			}
			            
			WorkShopAllowItems.Clear();
			if (assets == null) assets = new GameObject("WebObject").AddComponent<ImageAssets>();
			
			foreach(var item in ItemManager.itemList)
			{
				if (!ItemShortNames.Contains(item.shortname))
					ItemShortNames.Add(item.shortname);
				
				var workshopName = item.displayName.english.ToLower().Replace("skin", "").Replace(" ", "").Replace("-", "");
                if (!workshopNameToShortname.ContainsKey(workshopName))
                    workshopNameToShortname.Add(workshopName, item.shortname);
			}						
			
            isInitialized = true;
			
			AddImage("http://i.imgur.com/sZepiWv.png", "NONE", 0);
            AddImage("http://i.imgur.com/lydxb0u.png", "LOADING", 0);
			
			if (LastCEID != instanceID)			
				RestoreOldCache();											
			
			var pack = new List<List<object>>();
			foreach(var name in ItemShortNames)			
				pack.Add(new List<object>() {null, name, 0l, null});									
			
			AddImagePack("Обновление стандартных скинов", pack);
			
			GetWorkshopAllowSkinsInfo();
			
			if (configData.EnableStoreAvatars)
				foreach (var player in BasePlayer.activePlayerList)
					OnPlayerConnected(player);
        }
		
		private void OnPlayerConnected(BasePlayer player) 
		{
			if (configData.EnableStoreAvatars)
				AddImage(null, null, player.userID);
		}

        private void Unload()
        {
            SaveStandartSkinsData();
			SaveWorkshopSkinsData();
			SavePlayerAvatarsData();
			SaveCustomImagesData();
			
            UnityEngine.Object.Destroy(assets);
            instance = null;
			
			WorkShopAllowItems.Clear();
        }
		
		private void OnServerSave()
        {
			if (NeedStandartSave)
			{
				NeedStandartSave = false;
				SaveStandartSkinsData();
			}			
			
			if (NeedWorkshopSkinsSave)
			{
				NeedWorkshopSkinsSave = false;
				SaveWorkshopSkinsData();
			}
			
			if (NeedPlayerAvatarsSave)
			{
				NeedPlayerAvatarsSave = false;
				SavePlayerAvatarsData();
			}
			
			if (NeedCustomImagesSave)
			{
				NeedCustomImagesSave = false;
				SaveCustomImagesData();
			}			
        }
		
		#endregion
		
		#region Gather Images
		
		private static List<Rust.Workshop.ItemSchema.Item> GetSkinItemsInfo()
		{
			var result = new List<Rust.Workshop.ItemSchema.Item>();
			var aproved = Rust.Workshop.Approved.All.Select(x=> x.Value).ToList();
			
			foreach (var itemRaw in aproved)
			{
				var item = new Rust.Workshop.ItemSchema.Item();
				item.itemshortname = itemRaw.Skinnable.ItemName;
				item.workshopid = itemRaw.WorkshopdId > 0 ? itemRaw.WorkshopdId.ToString() : null;
				item.workshopdownload = item.workshopid;
				item.name = itemRaw.Name;
				item.itemdefid = (uint)itemRaw.InventoryId;
				item.icon_url = "https://files.facepunch.com/rust/icons/inventory/rust/" + itemRaw.InventoryId + "_small.png";
				result.Add(item);
			}
			
			return result;
		}
		
		private void GetWorkshopAllowSkinsInfo()
		{	
			foreach (var item in GetSkinItemsInfo())
			{
				if (string.IsNullOrEmpty(item.icon_url)) continue;
				var url = item.icon_url.Substring(0, item.icon_url.IndexOf(".png")+4);
				if (string.IsNullOrEmpty(url) || !url.EndsWith(".png")) continue;
				var skinIDString = !string.IsNullOrEmpty(item.workshopid) ? item.workshopid : item.itemdefid.ToString();
				if (string.IsNullOrEmpty(skinIDString)) continue;
				
				ulong skinID = 0l;
				try { skinID = (ulong)Convert.ToInt64(skinIDString); } catch { }
				if (skinID == 0l) continue;
				
				if (WorkShopAllowItems.ContainsKey(skinID)) continue;						
				
				var info = new WorkshopSkinInfo();
				info.Url = url;
				info.Name = item.name;
				info.shortname = item.itemshortname;

				WorkShopAllowItems.Add(skinID, info);
			}										
			
			LoadOldVerWorkshopSkins();
			
			if (configData.EnableStoreAllowWorkshopSkins)
				LoadAllowWorkshopSkins();
		}				
		
		private static string GetShortNameByTag(List<SteamSkinPublishDet.Tag> tags)
		{
			foreach (var tag in tags)
			{
				var adjTag = tag.tag.ToLower().Replace("skin", "").Replace(" ", "").Replace("-", "");
				if (workshopNameToShortname.ContainsKey(adjTag))
					return workshopNameToShortname[adjTag];
			}
			
			return null;
		}
		
		private void LoadOldVerWorkshopSkins()
		{
			var list = new Dictionary<string, Link>();
			
			foreach(var pair in WorkShopAllowItems.Where(x=> !HasImage(null, x.Key) && x.Key < 1000000))
				list.Add(pair.Key.ToString(), new Link(null, pair.Value.Url, pair.Value.Name, pair.Value.shortname));
			
			if (list.Count == 0) return;
			
			assets.AddOrder(new LoadOrder("разрешенных старых версий WorkShop скинов", "workshop", list));
		}
		
		private void LoadAllowWorkshopSkins()
		{
			var list = new Dictionary<string, Link>();
			
			foreach(var pair in WorkShopAllowItems.Where(x=> !HasImage(null, x.Key) && x.Key >= 1000000))
				list.Add(pair.Key.ToString(), new Link(null, pair.Value.Url, pair.Value.Name, pair.Value.shortname));
			
			if (list.Count == 0) return;
			
			assets.AddOrder(new LoadOrder("разрешенных новых версий WorkShop скинов", "workshop", list));
		}
		
		private void RestoreOldCache()
		{			
			var oldStandartSkinsFiles = new Dictionary<string, Link>();			
			foreach (var pair in StandartSkins.ToDictionary(x=> x.Key, x=> x.Value))
			{
				if (!string.IsNullOrEmpty(pair.Value.Cache) && IsInStorage(pair.Value.Cache, LastCEID))
				{
					var bytes = FileStorage.server.Get(uint.Parse(pair.Value.Cache), FileStorage.Type.png, LastCEID);
					oldStandartSkinsFiles.Add(pair.Key, new Link(bytes, pair.Value.Url, pair.Value.Name));
				}
				else
					StandartSkins.Remove(pair.Key);
			}
		
			var oldWorkshopSkinsFiles = new Dictionary<string, Link>();			
			foreach (var pair in WorkshopSkins.ToDictionary(x=> x.Key, x=> x.Value))
			{
				if (!string.IsNullOrEmpty(pair.Value.Cache) && IsInStorage(pair.Value.Cache, LastCEID))
				{
					var bytes = FileStorage.server.Get(uint.Parse(pair.Value.Cache), FileStorage.Type.png, LastCEID);
					oldWorkshopSkinsFiles.Add(pair.Key.ToString(), new Link(bytes, pair.Value.Url, pair.Value.Name));
				}
				else
					WorkshopSkins.Remove(pair.Key);
			}
		
			var oldPlayerAvatarsFiles = new Dictionary<string, Link>();			
			foreach (var pair in PlayerAvatars.ToDictionary(x=> x.Key, x=> x.Value))
			{
				if (!string.IsNullOrEmpty(pair.Value.Cache) && IsInStorage(pair.Value.Cache, LastCEID))
				{
					var bytes = FileStorage.server.Get(uint.Parse(pair.Value.Cache), FileStorage.Type.png, LastCEID);
					oldPlayerAvatarsFiles.Add(pair.Key.ToString(), new Link(bytes, pair.Value.Url, null));
				}
				else
					PlayerAvatars.Remove(pair.Key);
			}
		
			var oldCustomImagesFiles = new Dictionary<string, Link>();			
			foreach (var pair in CustomImages.ToDictionary(x=> x.Key, x=> x.Value))
			{
				if (!string.IsNullOrEmpty(pair.Value.Cache) && IsInStorage(pair.Value.Cache, LastCEID))
				{
					var bytes = FileStorage.server.Get(uint.Parse(pair.Value.Cache), FileStorage.Type.png, LastCEID);
					oldCustomImagesFiles.Add(pair.Key, new Link(bytes, pair.Value.Url, pair.Value.Name));
				}
				else
					CustomImages.Remove(pair.Key);
			}
			
			Facepunch.Sqlite.Database db = new Facepunch.Sqlite.Database();
            try
            {
                db.Open($"{ConVar.Server.rootFolder}/sv.files.0.db");
                db.Execute("DELETE FROM data WHERE entid = ?", LastCEID);
                db.Close();
            }
            catch { }
			
			if (oldStandartSkinsFiles.Count > 0)
				assets.AddOrder(new LoadOrder("Перенос стандартных скинов", "standart", oldStandartSkinsFiles));
			
			if (oldWorkshopSkinsFiles.Count > 0)
				assets.AddOrder(new LoadOrder("Перенос WorkShop скинов", "workshop", oldWorkshopSkinsFiles));
			
			if (oldPlayerAvatarsFiles.Count > 0)
				assets.AddOrder(new LoadOrder("Перенос аватарок игроков", "player", oldPlayerAvatarsFiles));
			
			if (oldCustomImagesFiles.Count > 0)
				assets.AddOrder(new LoadOrder("Перенос кастомных изображений", "custom", oldCustomImagesFiles));
			
			LastCEID = instanceID;
            SaveLastCEIDData();			
		}
		
		private IEnumerator ClearDataByTypeAndInit(BasePlayer player, string type)
		{
			var wasDefault = false;
			
			switch (type.ToLower())
			{
				case "standart": 
				{
					Reply(player, "Запущена чистка и реинициализация стандартныйх скинов.");
					foreach (var item in StandartSkins.ToDictionary(x=> x.Key, x=> x.Value))
					{						
						if (!HasImage(item.Key, 0l)) continue;
						
						var cache = item.Value.Cache;						
						uint crc = uint.Parse(cache);
						FileStorage.server.Remove(crc, FileStorage.Type.png, instanceID);
						
						StandartSkins.Remove(item.Key);
						yield return new WaitForSeconds(0.01f);
					}															
					NeedStandartSave = true;
					break;
				}
				case "workshop": 
				{
					Reply(player, "Запущена чистка и реинициализация скинов из workshop.");
					foreach (var item in WorkshopSkins.ToDictionary(x=> x.Key, x=> x.Value))
					{						
						if (!HasImage(null, item.Key)) continue;
						
						var cache = item.Value.Cache;						
						uint crc = uint.Parse(cache);
						FileStorage.server.Remove(crc, FileStorage.Type.png, instanceID);
						
						WorkshopSkins.Remove(item.Key);
						yield return new WaitForSeconds(0.01f);
					}
					NeedWorkshopSkinsSave = true;
					break;
				}
				case "player": 
				{
					Reply(player, "Запущена чистка и реинициализация аватарок игроков.");
					foreach (var item in PlayerAvatars.ToDictionary(x=> x.Key, x=> x.Value))
					{						
						if (!HasImage(null, item.Key)) continue;
						
						var cache = item.Value.Cache;						
						uint crc = uint.Parse(cache);
						FileStorage.server.Remove(crc, FileStorage.Type.png, instanceID);
						
						PlayerAvatars.Remove(item.Key);
						yield return new WaitForSeconds(0.01f);
					}
					NeedPlayerAvatarsSave = true;
					break;
				}
				case "custom": 
				{
					Reply(player, "Запущена чистка и реинициализация кастомных картинок.");
					foreach (var item in CustomImages.ToDictionary(x=> x.Key, x=> x.Value))
					{						
						if (!HasImage(item.Key, 0l)) continue;
						
						var cache = item.Value.Cache;						
						uint crc = uint.Parse(cache);
						FileStorage.server.Remove(crc, FileStorage.Type.png, instanceID);
						
						CustomImages.Remove(item.Key);
						yield return new WaitForSeconds(0.01f);
					}
					NeedCustomImagesSave = true;
					break;
				}
				default:
				{
					Reply(player, "Ошибка, используйте один из перечисленных параметров: standart, workshop, player, custom");					
					wasDefault = true;
					break;
				}
			}
			
			if (!wasDefault)
			{
				Reply(player, "Чистка данных завершена.");			
				OnServerInitialized();
			}
		}
		
		#endregion
		
		#region API
		
		/* Добавляет картинку базируясь сперва на переданном url или массиве байтов, а если там пусто то пытается сам загрузить или скин или аватарку */
		/* callback - вызывается только при успешной операции, а если картинка уже была ранее загружена, то callback не будет вызван так же */
		/* Данный метод подходит для предварительной загрузки картинки (без вывода её содержимого на экран), для ускорения последующего доступа к ней */
		/* Первый параметр при вызове стоит явно приводить к object типу, что бы данный метод точно вызвался */
		[HookMethod("AddImage")]
        public bool AddImage(object url_bytes, string imageName, ulong imageId, string fullName = null, bool replace = false, Action callback = null)
        {
			if (!isInitialized) 
				return false;
			
			string url = url_bytes is string ? url_bytes as string : null;
			byte[] bytes = url_bytes is byte[] ? url_bytes as byte[] : null;
			
			LoadOrder order;						
			
			if (imageId > 0)
			{				
				if (imageId.IsSteamId() && PlayerAvatars.ContainsKey(imageId) && !string.IsNullOrEmpty(PlayerAvatars[imageId].Cache) && IsInStorage(PlayerAvatars[imageId].Cache) ||
					WorkshopSkins.ContainsKey(imageId) && !string.IsNullOrEmpty(WorkshopSkins[imageId].Cache) && IsInStorage(WorkshopSkins[imageId].Cache))
					if (!replace) return true;
				
				if (imageId.IsSteamId())									
					order = new LoadOrder(null, "player", new Dictionary<string, Link> { { $"{imageId}", new Link(bytes, url, fullName) } }, callback);
				else
					order = new LoadOrder(null, "workshop", new Dictionary<string, Link> { { $"{imageId}", new Link(bytes, url, fullName) } }, callback);				
			}
			else
			{
				if (string.IsNullOrEmpty(imageName) || 
					StandartSkins.ContainsKey(imageName) && !string.IsNullOrEmpty(StandartSkins[imageName].Cache) && IsInStorage(StandartSkins[imageName].Cache) ||
					CustomImages.ContainsKey(imageName) && !string.IsNullOrEmpty(CustomImages[imageName].Cache) && IsInStorage(CustomImages[imageName].Cache))
					if (!replace || string.IsNullOrEmpty(imageName)) return true;
				
				if (ItemShortNames.Contains(imageName))									
					order = new LoadOrder(null, "standart", new Dictionary<string, Link> { { $"{imageName}", new Link(bytes, url, fullName) } }, callback);
				else
					order = new LoadOrder(null, "custom", new Dictionary<string, Link> { { $"{imageName}", new Link(bytes, url, fullName) } }, callback);
			}
						
			assets.AddOrder(order);
            return true;
        }
		
		/* Поддержка хука из старой библиотеки ImageLibrary */ 
		[HookMethod("AddImage")]
        public bool AddImage(string url, string imageName, ulong imageId, Action callback = null) => AddImage(url, imageName, imageId, null, true, callback);
		
		/* Поддержка хука из старой библиотеки ImageLibrary */
		[HookMethod("AddImageData")]
        public bool AddImageData(string imageName, byte[] array, ulong imageId, Action callback = null) => AddImage((object)array, imageName, imageId, null, true, callback);
		
		/* Тоже что и предыдущий метод, только выполняется загрузка пакетом (это будет выгодно при загрузке скинов с workshop, в остальных случаях подойдет и AddImage в цикле)*/
		/* Строки пакета в списке: string url_bytes, string imageName, ulong imageId, string fullName */
		[HookMethod("AddImagePack")]
        public bool AddImagePack(string title, List<List<object>> pack, bool replace = false, Action callback = null)
        {
			if (!isInitialized) 
				return false;
						
			var playerLinks = new Dictionary<string, Link>();
			var workshopLinks = new Dictionary<string, Link>();
			var standartLinks = new Dictionary<string, Link>();
			var customLinks = new Dictionary<string, Link>();
			
			foreach (var item in pack)
			{								
				string url = item[0] is string ? item[0] as string : null;
				byte[] bytes = item[0] is byte[] ? item[0] as byte[] : null;				
				string imageName = item[1] is string ? item[1] as string : null;
				string fullName = item[3] is string ? item[3] as string : null;
								
				ulong imageId = 0l;
				try { imageId = (ulong)Convert.ToInt64(item[2]); } catch { continue; }				
				
				if (imageId > 0)
				{				
					if (imageId.IsSteamId() && PlayerAvatars.ContainsKey(imageId) && !string.IsNullOrEmpty(PlayerAvatars[imageId].Cache) && IsInStorage(PlayerAvatars[imageId].Cache) ||
						WorkshopSkins.ContainsKey(imageId) && !string.IsNullOrEmpty(WorkshopSkins[imageId].Cache) && IsInStorage(WorkshopSkins[imageId].Cache))
						if (!replace) continue;
					
					if (imageId.IsSteamId())
						playerLinks.Add($"{imageId}", new Link(bytes, url, fullName));
					else											
						workshopLinks.Add($"{imageId}", new Link(bytes, url, fullName));
				}
				else
				{
					if (string.IsNullOrEmpty(imageName) || 
						StandartSkins.ContainsKey(imageName) && !string.IsNullOrEmpty(StandartSkins[imageName].Cache) && IsInStorage(StandartSkins[imageName].Cache) ||
						CustomImages.ContainsKey(imageName) && !string.IsNullOrEmpty(CustomImages[imageName].Cache) && IsInStorage(CustomImages[imageName].Cache))
						if (!replace || string.IsNullOrEmpty(imageName)) continue;										
					
					if (ItemShortNames.Contains(imageName))
						standartLinks.Add($"{imageName}", new Link(bytes, url, fullName));
					else
						customLinks.Add($"{imageName}", new Link(bytes, url, fullName));						
				}				
			}
			
			int diffCount = (playerLinks.Count > 0 ? 1 : 0) + (workshopLinks.Count > 0 ? 1 : 0) + (standartLinks.Count > 0 ? 1 : 0) + (customLinks.Count > 0 ? 1 : 0);
			int count = diffCount;
			
			if (playerLinks.Count > 0)
			{				
				assets.AddOrder(new LoadOrder(title + (diffCount > 1 ? " (аватарки игроков)" : ""), "player", playerLinks, count == 1 ? callback : null));
				count--;
			}
			
			if (workshopLinks.Count > 0)
			{				
				assets.AddOrder(new LoadOrder(title + (diffCount > 1 ? " (скины из WorkShop)" : ""), "workshop", workshopLinks, count == 1 ? callback : null));
				count--;
			}
			
			if (standartLinks.Count > 0)
			{				
				assets.AddOrder(new LoadOrder(title + (diffCount > 1 ? " (стандартные скины)" : ""), "standart", standartLinks, count == 1 ? callback : null));
				count--;
			}
			
			if (customLinks.Count > 0)
			{				
				assets.AddOrder(new LoadOrder(title + (diffCount > 1 ? " (кастомные картинки)" : ""), "custom", customLinks, count == 1 ? callback : null));
				count--;
			}
			
			if (diffCount == 0 && callback != null)
				callback.Invoke();				
			
            return true;
        }				
		
		/* Возвращает либо кеш, либо url картинки, а если картинка еще не была загружена, то делается попытка её загрузить и возвращается картинка "ожидания" */
		/* Данный метод подходит для предварительной загрузки картинки (без вывода её содержимого на экран), для ускорения последующего доступа к ней */
		[HookMethod("GetImage")]
        public string GetImage(string imageName, ulong imageId = 0, bool returnUrl = false)
        {
			if (!isInitialized || imageId == 0 && string.IsNullOrEmpty(imageName)) 
				return returnUrl ? GetCustomSkin("NONE")?.Url : GetCustomSkin("NONE")?.Cache;
			
			string url = null;
			string loadType = null;			
			
			if (imageId > 0)
			{
				if (imageId.IsSteamId())
				{
					if (PlayerAvatars.ContainsKey(imageId))
					{
						if (returnUrl)
						{
							if (!string.IsNullOrEmpty(PlayerAvatars[imageId].Url))
								return PlayerAvatars[imageId].Url;							
						}
						else
						{
							if (!string.IsNullOrEmpty(PlayerAvatars[imageId].Cache) && IsInStorage(PlayerAvatars[imageId].Cache))
								return PlayerAvatars[imageId].Cache;
							else
								url = PlayerAvatars[imageId].Url;
						}
					}
					else
						loadType = "player";
				}
				else
				{
					if (WorkshopSkins.ContainsKey(imageId))
					{
						if (returnUrl)
						{
							if (!string.IsNullOrEmpty(WorkshopSkins[imageId].Url))
								return WorkshopSkins[imageId].Url;							
						}
						else
						{
							if (!string.IsNullOrEmpty(WorkshopSkins[imageId].Cache) && IsInStorage(WorkshopSkins[imageId].Cache))
								return WorkshopSkins[imageId].Cache;
							else
								url = WorkshopSkins[imageId].Url;							
						}
					}
					else
						loadType = "workshop";
				}					 																							
			}
			else
			{
				if (ItemShortNames.Contains(imageName))
				{
					if (StandartSkins.ContainsKey(imageName))
					{
						if (returnUrl)
						{
							if (!string.IsNullOrEmpty(StandartSkins[imageName].Url))
								return StandartSkins[imageName].Url;							
						}
						else
						{
							if (!string.IsNullOrEmpty(StandartSkins[imageName].Cache) && IsInStorage(StandartSkins[imageName].Cache))
								return StandartSkins[imageName].Cache;
							else
								url = StandartSkins[imageName].Url;
						}
					}
					else
						loadType = "standart";
				}
				else
				{
					if (CustomImages.ContainsKey(imageName))
					{
						if (returnUrl)
						{
							if (!string.IsNullOrEmpty(CustomImages[imageName].Url))
								return CustomImages[imageName].Url;							
						}
						else
						{
							if (!string.IsNullOrEmpty(CustomImages[imageName].Cache) && IsInStorage(CustomImages[imageName].Cache))
								return CustomImages[imageName].Cache;
							else
								url = CustomImages[imageName].Url;
						}
					}					
				}								
			}	
			
			switch (loadType)
			{
				case "player": AddImage(null, null, imageId); break;							   
				case "workshop": AddImage(null, null, imageId); break;
				case "standart": AddImage(null, imageName, 0l); break;								 
			}
			
			if (!string.IsNullOrEmpty(loadType))
				return returnUrl ? GetCustomSkin("LOADING")?.Url : GetCustomSkin("LOADING")?.Cache;
			
			if (string.IsNullOrEmpty(url))
				return returnUrl ? GetCustomSkin("NONE")?.Url : GetCustomSkin("NONE")?.Cache;							
			
			AddImage(url, imageName, imageId);
            return returnUrl ? GetCustomSkin("LOADING")?.Url : CustomImages["LOADING"].Cache;
        }
		
		/* То же что и GetImage только чисто возвращает url */
		[HookMethod("GetImageURL")]
        public string GetImageURL(string imageName, ulong imageId = 0) => GetImage(imageName, imageId, true);        
		
		/* Возвращает список ИД скинов по shortname предмета.Возвращаются только те скины, которые были загружены заранее и есть в дата файле */
		[HookMethod("GetImageList")]
        public List<ulong> GetImageList(string shortname) => WorkshopSkins.Where(x=> x.Value.shortname == shortname).Select(x=> x.Key).ToList();        
		
		/* Возвращает структуру с данными по скину, но по факту тут есть только название предмета от автора скина, остальные поля пусты (оставлены для совместимости со старым плагином) */
		[HookMethod("GetSkinInfo")]
        public Dictionary<string, object> GetSkinInfo(string imageName, ulong imageId)
        {
			var skinInfo = new Dictionary<string, object>();
			skinInfo.Add("votesup", 0);
			skinInfo.Add("votesdown", 0);
			skinInfo.Add("score", 0);
			skinInfo.Add("views", 0);
			skinInfo.Add("created", new DateTime());
			skinInfo.Add("description", "");
			
			if (imageId > 0)
			{
				if (WorkshopSkins.ContainsKey(imageId) && !string.IsNullOrEmpty(WorkshopSkins[imageId].Name))
				{					
					skinInfo.Add("title", WorkshopSkins[imageId].Name);
					return skinInfo;
				}
			}
			else
			{
				if (StandartSkins.ContainsKey(imageName) && !string.IsNullOrEmpty(StandartSkins[imageName].Name))
				{
					skinInfo.Add("title", StandartSkins[imageName].Name);
					return skinInfo;
				}
				
				if (CustomImages.ContainsKey(imageName) && !string.IsNullOrEmpty(CustomImages[imageName].Name))
				{
					skinInfo.Add("title", CustomImages[imageName].Name);
					return skinInfo;
				}
			}
			
            return null;
        }				
		
		/* Проверка на существование картинки как в дата файлах, так и в кеше раста.Если картинка где то из этих двух мест отсутствует, то возвращает false */
		[HookMethod("HasImage")]
        public bool HasImage(string imageName, ulong imageId)
        {
			if (!isInitialized || imageId == 0 && string.IsNullOrEmpty(imageName)) 
				return false;
			
			if (imageId > 0)
			{
				if (imageId.IsSteamId() && PlayerAvatars.ContainsKey(imageId) && !string.IsNullOrEmpty(PlayerAvatars[imageId].Cache) && IsInStorage(PlayerAvatars[imageId].Cache) ||
					WorkshopSkins.ContainsKey(imageId) && !string.IsNullOrEmpty(WorkshopSkins[imageId].Cache) && IsInStorage(WorkshopSkins[imageId].Cache))
					return true;
			}
			else
			{
				if (StandartSkins.ContainsKey(imageName) && !string.IsNullOrEmpty(StandartSkins[imageName].Cache) && IsInStorage(StandartSkins[imageName].Cache) ||
					CustomImages.ContainsKey(imageName) && !string.IsNullOrEmpty(CustomImages[imageName].Cache) && IsInStorage(CustomImages[imageName].Cache))
					return true;
			}			            

            return false;
        }

		/* проверяет входит ли указанный кеш в хранилище с кешами раста */
		public bool IsInStorage(string crc) 
		{
			if (string.IsNullOrEmpty(crc)) 
				return false;
			
			return FileStorage.server.Get(uint.Parse(crc), FileStorage.Type.png, instanceID) != null;
		}
		
		/* Возвращает статус загрузки картинок */
		[HookMethod("IsReady")]
        public bool IsReady() => isInitialized && assets.IsReady();		
		
		/* Функция удаляет кастомные картинки (удаление скинов и аватарок запрещено) */
		/* Если было загружено некорректное изображение скина или аватарки, его следует перезагрузить новым, корректным значением */
		[HookMethod("RemoveImage")]
        public void RemoveImage(string imageName, ulong imageId)
		{
			if (imageId > 0 || StandartSkins.ContainsKey(imageName) || !HasImage(imageName, imageId)) return;
			
			var cache = CustomImages[imageName].Cache;
			uint crc = uint.Parse(cache);
            FileStorage.server.Remove(crc, FileStorage.Type.png, instanceID);
			
			CustomImages.Remove(imageName);
			
			NeedCustomImagesSave = true;
		}
		
		/* Заглушка для хука из старой библиотеки ImageLibrary (не поддерживается, используйте AddImagePack) */
		[HookMethod("ImportImageList")]
        public void ImportImageList(string title, Dictionary<string, string> imageList, ulong imageId = 0, bool replace = false, Action callback = null) { }
        
		/* Заглушка для хука из старой библиотеки ImageLibrary (не поддерживается, используйте AddImagePack) */
		[HookMethod("ImportItemList")]
        public void ImportItemList(string title, Dictionary<string, Dictionary<ulong, string>> itemList, bool replace = false, Action callback = null) { }
        		
		/* Заглушка для хука из старой библиотеки ImageLibrary (не поддерживается, используйте AddImagePack) */		
		[HookMethod("ImportImageData")]
        public void ImportImageData(string title, Dictionary<string, byte[]> imageList, ulong imageId = 0, bool replace = false, Action callback = null) { }		
				
		/* Заглушка для хука из старой библиотеки ImageLibrary (не поддерживается, используйте AddImagePack) */				
		[HookMethod("LoadImageList")]
        public void LoadImageList(string title, List<KeyValuePair<string, ulong>> imageList, Action callback = null) { }		
				
		#endregion

		#region Image Storage				
		
		private bool IsInStorage(string crc, uint CEID) 
		{
			if (string.IsNullOrEmpty(crc) || CEID == 0)
				return false;
			
			return FileStorage.server.Get(uint.Parse(crc), FileStorage.Type.png, CEID) != null;
		}

        private struct LoadOrder
        {
            public string loadName;            
			public string type; // standart, workshop, player, custom           
			
			public Dictionary<string, Link> imageDataLink;

            public Action callback;
                       
			public LoadOrder(string loadName, string type, Dictionary<string, Link> imageData, Action callback = null)
            {
                this.loadName = loadName;
				this.type = type;
                this.imageDataLink = imageData;                
                this.callback = callback;				
            }
        }
		
		private class Link
		{
			public byte[] raw;
			public string url;
			public string name;
			public string shortname;
			
			public Link(byte[] raw, string url, string name)
			{
				this.raw = raw;
				this.url = url;
				this.name = name;
				this.shortname = null;
			}
			public Link(byte[] raw, string url, string name, string shortname)
			{
				this.raw = raw;
				this.url = url;
				this.name = name;
				this.shortname = shortname;
			}
		}

        private class ImageAssets : MonoBehaviour
        {
            private Queue<QueueItem> queueList = new Queue<QueueItem>();
			private Queue<LoadOrder> loadOrders = new Queue<LoadOrder>();
			private Dictionary<int, Result> Waiting = new Dictionary<int, Result>();
			
			private class Result
			{
				public bool isEnded;
				public List<SteamSkinPublishDet.Response.Publishedfiledetail> dList = new List<SteamSkinPublishDet.Response.Publishedfiledetail>();
			}
			
            private string request;
			private Action callback;
			private int saveMask; // 1 - standart, 2 - workshop, 4 - player, 8 - custom
			private bool isBusy, isAbort;			

            private void OnDestroy() 
			{
				InvokeHandler.CancelInvoke(this, Activity);
				queueList.Clear();
				loadOrders.Clear();
				saveMask = 0;
				request = null;
				callback = null;
				isBusy = false;
			}
			
			public void AddOrder(LoadOrder order) => loadOrders.Enqueue(order);
			
			public bool IsReady() => isInitialized && loadOrders?.Count == 0 && !isBusy;
			
			public string GetCurrentTaskName() 
			{
				if (!IsReady())
				{
					if (!string.IsNullOrEmpty(request))
						return request;										
				}
				
				return "N|A";
			}
			
			public void AbortLoading()
			{
				if (IsReady()) return;
				
				loadOrders.Clear();
				queueList.Clear();				
				isAbort = true;
				
				if (string.IsNullOrEmpty(request))
					instance.PrintWarning($"Загрузка изображений была прервана!");
			}
			
			private void Awake() => InvokeHandler.InvokeRepeating(this, Activity, 1f, 1f);
			
			private void Activity()
			{
				if (instance == null)
				{
					UnityEngine.Object.Destroy(this);
					return;
				}					
					
				if (!isInitialized || isBusy || loadOrders?.Count == 0) return;
				
				isBusy = true;
				
				var nextLoad = loadOrders.Dequeue();
				
				request = nextLoad.loadName;
				
                if (!string.IsNullOrEmpty(request))
                    instance.Puts($"Начинаем пакетную загрузку изображений '{request}'");

				callback = nextLoad.callback;				
				StartCoroutine(CheckNeedWorkShopSkinsAndRunLoad(nextLoad));				
			}						

            private void AddQueueItem(string name, string fullName, string type, string url, byte[] bytes, string shortname) => queueList.Enqueue(new QueueItem(name, type, fullName, url, bytes, shortname));

            private void Next()
            {								
                if (queueList.Count == 0)
                {                                        
					if ((saveMask & 1) == 1)
						NeedStandartSave = true; 
					
					if ((saveMask & 2) == 2)
						NeedWorkshopSkinsSave = true;
					
					if ((saveMask & 4) == 4)
						NeedPlayerAvatarsSave = true;
					
					if ((saveMask & 8) == 8)
						NeedCustomImagesSave = true;
					
					saveMask = 0;										
					
                    if (!string.IsNullOrEmpty(request))
						if (!isAbort)
							instance.Puts($"Пакетная загрузка изображений '{request}' успешно завершена.");
						else
							instance.PrintWarning($"Пакетная загрузка изображений '{request}' была прервана!");											

					isBusy = false;	
						
                    request = string.Empty;                    
                    if (callback != null) callback.Invoke();
					
					isAbort = false;
					
                    return;
                }                               

                QueueItem queueItem = queueList.Dequeue();
				
				if (queueItem.bytes == null || queueItem.bytes.Length == 0)
				{
					if (!string.IsNullOrEmpty(queueItem.url))
						StartCoroutine(DownloadImage(queueItem));
					else
					{
						if (queueItem.type == "player")
							StartCoroutine(DownloadPlayerAvatarUrl(queueItem));
						else
							if (queueItem.type == "standart")
							{
								queueItem.url = $"https://rustlabs.com/img/items180/{queueItem.name}.png";
								StartCoroutine(DownloadImage(queueItem));
							}
							else
							{
								var who = queueItem.type == "custom" ? "workshop скина" : "кастомной картинки";
								instance.PrintWarning($"Ошибка скачивания {who} '{queueItem.name}'! Не наден url для загрузки." );
								Next();
							}
					}
				}
                else                
					StoreByteArray(queueItem);
            }
			
			private IEnumerator DownloadPlayerAvatarUrl(QueueItem queueItem)
			{
				ulong userID = 0l;
				try { userID = (ulong)Convert.ToInt64(queueItem.name); } catch {}
				if (userID == 0l)
				{
					Next();
                    yield break;
				}

				UnityWebRequest www = UnityWebRequest.Get($"http://steamcommunity.com/profiles/{userID}?xml=1");

                yield return www.SendWebRequest();
                if (instance == null) yield break;
                if (www.isNetworkError || www.isHttpError)
                {
                    instance.PrintWarning(string.Format("Ошибка скачивания аватарки! Ошибка: {0}, Steam ID игрока {1}", www.error, queueItem.name));
                    www.Dispose();
					                                        
					Next();
                    yield break;
                }
				
				string avatar = avatarFilter.Match(www.downloadHandler.text).Groups[1].ToString();
				if (!string.IsNullOrEmpty(avatar))
				{
					queueItem.url = avatar;
					StartCoroutine(DownloadImage(queueItem));					
				}
				else
					Next();
				
				www.Dispose();
			}
			
			private void RunWorkShopSkinsDetLoading(List<ulong> skinIds, int waitKey)
			{
				var str = string.Format("?key={0}&itemcount={1}", configData.SteamApiKey, skinIds.Count);
				int cnt = 0;
				foreach (var skin in skinIds)
				{
					str += string.Format("&publishedfileids[{0}]={1}", cnt, skin);
					cnt++;
				}
				
				try
				{
					instance.webrequest.Enqueue(SteamPublishedSkinsURL, str, (code, response) => 
					{
						if (!(response == null || code != 200))
						{
							var data = JsonConvert.DeserializeObject<SteamSkinPublishDet>(response);
							if (!(data == null || !(data is SteamSkinPublishDet) || data.response.result == 0 || data.response.resultcount == 0))						
								Waiting[waitKey].dList = data.response.publishedfiledetails;
						}
						
						Waiting[waitKey].isEnded = true;
					}, instance, RequestMethod.POST);
				}
				catch 
				{ 
					Waiting[waitKey].isEnded = true;
				}
			}
			
			private IEnumerator CheckNeedWorkShopSkinsAndRunLoad(LoadOrder nextLoad)
			{
				if (nextLoad.imageDataLink != null && nextLoad.imageDataLink.Count > 0)
				{
					if (nextLoad.type == "workshop")
					{
						var skinIds = new List<ulong>();
						
						foreach (var pair in nextLoad.imageDataLink.Where(x=> string.IsNullOrEmpty(x.Value.url)))
						{
							ulong skinID = 0l;
							try { skinID = (ulong)Convert.ToInt64(pair.Key); } catch {}
							if (skinID == 0l) continue;
							
							if (!WorkshopSkins.ContainsKey(skinID) || string.IsNullOrEmpty(WorkshopSkins[skinID].Url))							
								skinIds.Add(skinID);
						}	
						 													
						if (skinIds.Count > 0)
						{
							int waitKey = 1;
							
							while (Waiting.ContainsKey(waitKey))							
								waitKey = Rnd.Next(1, 9999999);
							
							Waiting.Add(waitKey, new Result());														
							RunWorkShopSkinsDetLoading(skinIds, waitKey);
							
							yield return new WaitWhile(() => !Waiting[waitKey].isEnded);

							foreach (var item in Waiting[waitKey].dList)
							{				
								if (string.IsNullOrEmpty(item.preview_url)) continue;			
								var url = item.preview_url;
								if (string.IsNullOrEmpty(url)) continue;				
								var skinIDString = item.publishedfileid.ToString();
								if (string.IsNullOrEmpty(skinIDString)) continue;				
								
								ulong skinID = 0l;
								try { skinID = (ulong)Convert.ToInt64(skinIDString); } catch { }
								if (skinID == 0l) continue;				
								
								if (nextLoad.imageDataLink.ContainsKey(skinIDString))
								{
									nextLoad.imageDataLink[skinIDString].url = url;
									nextLoad.imageDataLink[skinIDString].name = item.title;
									nextLoad.imageDataLink[skinIDString].shortname = GetShortNameByTag(item.tags);
								}							
							}
						}
					}
										
					foreach (var item in nextLoad.imageDataLink)
						AddQueueItem(item.Key, item.Value.name, nextLoad.type, item.Value.url, item.Value.raw, item.Value.shortname);
                }
				
                Next();
			}

            private IEnumerator DownloadImage(QueueItem info)
            {
                UnityWebRequest www = UnityWebRequest.Get(info.url);

                yield return www.SendWebRequest();
                if (instance == null) yield break;
                if (www.isNetworkError || www.isHttpError)
                {
                    instance.PrintWarning(string.Format("Ошибка скачивания картинки! Ошибка: {0}, Название картинки: {1}, URL картинки: {2}", www.error, info.name, info.url));
                    www.Dispose();
					                                        
					Next();
                    yield break;
                }

                Texture2D texture = new Texture2D(2, 2);
                texture.LoadImage(www.downloadHandler.data);
                if (texture != null)
                {
                    byte[] bytes = texture.EncodeToPNG();
                    DestroyImmediate(texture);
                    StoreByteArray(info, bytes);
                }
				else
					Next();					
				
                www.Dispose();
            }

            private void StoreByteArray(QueueItem info, byte[] bytes = null)
            {
				switch (info.type)
				{
					case "standart": 
					{
						if (!StandartSkins.ContainsKey(info.name))
							StandartSkins.Add(info.name, new StdCtmSkinInfo());
						
						StandartSkins[info.name].Name = !string.IsNullOrEmpty(info.fullName) ? info.fullName : ItemManager.FindItemDefinition(info.name)?.displayName?.english;
						StandartSkins[info.name].Url = info.url;
						
						var bytes_ = bytes != null && bytes.Length > 0 ? bytes : info.bytes;
						
						if (bytes_ != null && bytes_.Length > 0)
							StandartSkins[info.name].Cache = FileStorage.server.Store(bytes_, FileStorage.Type.png, instanceID).ToString();
						
						if ((saveMask & 1) != 1) saveMask += 1;
						
						break;
					}
					case "workshop": 
					{
						ulong skinID = 0l;
						try { skinID = (ulong)Convert.ToInt64(info.name); } catch {}
						if (skinID == 0l) break;
						
						if (!WorkshopSkins.ContainsKey(skinID))
							WorkshopSkins.Add(skinID, new WorkshopSkinInfo());
						
						WorkshopSkins[skinID].Name = info.fullName;
						WorkshopSkins[skinID].Url = info.url;
						WorkshopSkins[skinID].shortname = info.shortname;
						
						var bytes_ = bytes != null ? bytes : info.bytes;
						
						if (bytes_ != null)
							WorkshopSkins[skinID].Cache = FileStorage.server.Store(bytes_, FileStorage.Type.png, instanceID).ToString();
						
						if ((saveMask & 2) != 2) saveMask += 2;
						
						break;
					}
					case "player": 
					{
						ulong userID = 0l;
						try { userID = (ulong)Convert.ToInt64(info.name); } catch {}
						if (userID == 0l) break;
						
						if (!PlayerAvatars.ContainsKey(userID))
							PlayerAvatars.Add(userID, new PlayerSkinInfo());
												
						PlayerAvatars[userID].Url = info.url;
						
						var bytes_ = bytes != null ? bytes : info.bytes;
						
						if (bytes_ != null)
							PlayerAvatars[userID].Cache = FileStorage.server.Store(bytes_, FileStorage.Type.png, instanceID).ToString();
						
						if ((saveMask & 4) != 4) saveMask += 4;
						
						break;
					}
					case "custom": 
					{
						if (!CustomImages.ContainsKey(info.name))
							CustomImages.Add(info.name, new StdCtmSkinInfo());
						
						CustomImages[info.name].Name = info.fullName;
						CustomImages[info.name].Url = info.url;
						
						var bytes_ = bytes != null ? bytes : info.bytes;
						
						if (bytes_ != null)
							CustomImages[info.name].Cache = FileStorage.server.Store(bytes_, FileStorage.Type.png, instanceID).ToString();
						
						if ((saveMask & 8) != 8) saveMask += 8;
						
						break;
					}
				}
				                                			                
				Next();
            }

            private class QueueItem
            {
				public string fullName;
				public string name;
				public string type;
                public byte[] bytes;
                public string url;
                public string shortname;
				
                public QueueItem(string name, string type, string fullName, string url, byte[] bytes, string shortname)
                {
					this.fullName = fullName;
					this.name = name;
					this.type = type;
                    this.bytes = bytes;
                    this.url = url;
					this.shortname = shortname;
                }
            }
        }

        #endregion Image Storage
		
		#region Commands
				
		/* Load Allow Workshop Skins - загружает все разрешенные скины из воркшопа, а если какие то уже были загружены, то их пропускает */
		/* Данная операция долгая, минут 20, потому её стоит выполнять когда на сервере мало людей */
		[ConsoleCommand("il.loadallowworkshopskins")]
        private void cmdLoadWorkshopSkins(ConsoleSystem.Arg args)
        {
            BasePlayer player = args.Player();
            if (player != null && !player.IsAdmin) return;
			
			LoadAllowWorkshopSkins();
        }
		
		/* Ready State - проверка статуса очереди загрузки картинок */
		[ConsoleCommand("il.readystate")]
        private void cmdIsLoadEmpty(ConsoleSystem.Arg args)
        {
            BasePlayer player = args.Player();
            if (player != null && !player.IsAdmin) return;
			
			Reply(player, IsReady() ? "очередь загрузки пуста" : $"в очереди загрузки есть задания {assets.GetCurrentTaskName()}");
		}
		
		/* Abort Loading - прерывает длительную загрузку картинок */
		[ConsoleCommand("il.abortloading")]
        private void cmdAbortLoading(ConsoleSystem.Arg args)
        {
            BasePlayer player = args.Player();
            if (player != null && !player.IsAdmin) return;
			
			if (IsReady())
			{
				Reply(player, "в очереди загрузки нет заданий для прерывания");
				return;
			}
			
			assets.AbortLoading();
		}
		
		/* Wipe And Init - 
// ... [Code preview truncated due to length. Full source in attached file] ...

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