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

FREE VyHub : 1.3.11 1.3.11

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// #define TESTING

#if TESTING
     // #define RUST
     // #define SEVENDAYSTODIE
#endif

using System;
using System.Collections;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using UnityEngine;
using UnityEngine.Networking;
using Time = UnityEngine.Time;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Oxide.Plugins
{
	[Info("VyHub", "VyHub", "1.3.11")]
	[Description(
		"VyHub plugin to manage and monetize your Rust / 7 Days to Die server.You can create your webstore for free with VyHub!")]
	public class VyHub : CovalencePlugin
	{
		#region Fields

		private string _serverBundleID = string.Empty;

		private const string 
			GameType = "STEAM",
			CMD_EDIT_CONFIG = "vh_config",
			CMD_SETUP_CONFIG = "vh_setup",
			CMD_WARN = "warn";

		private readonly List<UnityWebRequest> _activeRequests = new List<UnityWebRequest>();

		private readonly List<Coroutine> _coroutines = new List<Coroutine>();
		
		private HashSet<string> _warnedUsers = new HashSet<string>();
		
		#endregion

		#region Config

		private Configuration _config;

		private class Configuration
		{
			[JsonProperty("Advert Settings")]
			public AdvertSettings AdvertSettings = new AdvertSettings
			{
				Prefix = "[★] ",
				Interval = 180f
			};

			[JsonProperty("API Settings")]
			public APISettings API = new APISettings
			{
				URL = "https://api.vyhub.app/<name>/v1",
				Key = "Admin -> Settings -> Server -> Setup",
				ServerID = "Admin -> Settings -> Server -> Setup"
			};

			[JsonProperty("Notify chat when a user has been blocked?")] 
			public bool NotifyChatOnUserBlocked = true;
		}

		private class AdvertSettings
		{
			[JsonProperty("Prefix")]
			public string Prefix;

			[JsonProperty("Interval")]
			public float Interval;
		}

		private class APISettings
		{
			[JsonProperty("URL")] public string URL;

			[JsonProperty("Key")] public string Key;

			[JsonProperty("Server ID")]
			public string ServerID;

			[JsonIgnore] 
			public Dictionary<string, string> Headers;

			[JsonIgnore] 
			public string
				ServerEndpoint,
				FetchAdvertsEndpoint,
				UserEndpoint,
				UserActivityEndpoint,
				PlaytimeDefinitionEndpoint, 
				PlaytimeEndpoint,
				ServerBundleEndpoint,
				BansEndpoint,
				WarningsEndpoint,
				GroupEndpoint,
				UserRewardsEndpoint,
				SendRewardsEndpoint;

			public void InitOrUpdate()
			{
				Headers = new Dictionary<string, string>
				{
					["Content-Type"] = "application/json",
					["Authorization"] = $"Bearer {Key}",
					["Accept"] = "application/json"
				};

				ServerEndpoint = $"{URL}/server/{ServerID}";
				FetchAdvertsEndpoint = $"{URL}/advert/?active=true";
				UserEndpoint = $"{URL}/user/";
				UserActivityEndpoint = $"{URL}/server/{ServerID}/user-activity";
				PlaytimeDefinitionEndpoint = $"{URL}/user/attribute/definition";
				PlaytimeEndpoint = $"{URL}/user/attribute/";
				ServerBundleEndpoint = $"{URL}/server/bundle/";
				BansEndpoint = $"{URL}/ban/";
				WarningsEndpoint = $"{URL}/warning/";
				GroupEndpoint = $"{URL}/group/";
				UserRewardsEndpoint =
					$"{URL}/packet/reward/applied/user?active=true&foreign_ids=true&status=OPEN&for_server_id={ServerID}";
				SendRewardsEndpoint = $"{URL}/packet/reward/applied/";
			}
		}

		protected override void LoadConfig()
		{
			base.LoadConfig();
			try
			{
				_config = Config.ReadObject<Configuration>();
				if (_config == null) throw new Exception();
				SaveConfig();
			}
			catch
			{
				PrintError("Your configuration file contains an error.Using default configuration values.");
				LoadDefaultConfig();
			}
		}

		protected override void SaveConfig()
		{
			Config.WriteObject(_config);
		}

		protected override void LoadDefaultConfig()
		{
			_config = new Configuration();
		}

		#endregion

		#region Data

		#region Users

		private Dictionary<string, VyHubUser> _vyHubUsers = new Dictionary<string, VyHubUser>();

		public VyHubUser GetVyHubUser(string targetID)
		{
			VyHubUser user;
			return _vyHubUsers.TryGetValue(targetID, out user) ? user : null;
		}

		private void LoadPlayerData(string targetID, Action<VyHubUser> callback = null)
		{
			GetOrCreateUser(targetID, user => callback?.Invoke(user));
		}

		private void UnloadPlayerData(string targetID)
		{
			_vyHubUsers.Remove(targetID);
		}

		#endregion

		#region Rewards

		private void SaveExecutedRewards()
		{
			SaveObjectToData("ExecutedRewards", _executedRewards);
		}

		private void LoadExecutedRewards()
		{
			try
			{
				_executedRewards = Interface.Oxide.DataFileSystem.ReadObject<List<string>>($"{Name}/ExecutedRewards");
			}
			catch (Exception e)
			{
				PrintError(e.ToString());
			}

			if (_executedRewards == null) _executedRewards = new List<string>();
		}

		#endregion

		#endregion

		#region Hooks

		private void Init()
		{
			LoadExecutedRewards();

			LoadCachedBans();
			
#if SEVENDAYSTODIE
			LoadCrossIDs();
#endif
		}

		private void OnServerInitialized()
		{
			RegisterPermissions();
			
			RegisterCommands();

			_config.API.InitOrUpdate();

#if RUST
			InitColors();
#else
			InitWebEngine();
#endif
			
			GetServerInformation(() =>
			{
				GetPlaytimeDefinition(() =>
				{
					StartTimers();

					InitAdverts();

					LoadPlayers();
				});
			});
		}

		private void Unload()
		{
			StopCoroutines();
			
#if RUST
			UnloadDashboardUIs();
#endif
			
			DisposeActiveRequests();

			DestroyTimers();

			SendPlayerTime(true);

			StopAdverts();

			SaveCachedBans();
	
#if !RUST
			DestroyWebEngine();
#endif
			
#if SEVENDAYSTODIE
			SaveCrossIDs();
#endif

			_config = null;
		}

		#region Players

		private void OnUserConnected(IPlayer player)
		{
			if (player == null) return;

			var playerID = player.Id;
			if (string.IsNullOrEmpty(playerID)) return;
			
			TryAddToPlayTimes(playerID);

#if SEVENDAYSTODIE
			TryToUpdateCrossID(playerID);
#endif
			
			GetOrCreateUser(playerID, user =>
			{
				if (user == null) return;
				
				GetUserRewards(user.ID, rewards =>
				{
					var reward = rewards.FirstOrDefault();
					if (reward.Value != null && reward.Value.Count > 0)
						_rewards[playerID] = reward.Value;

					ExecuteReward(new List<string>
					{
						"CONNECT",
						"SPAWN"
					}, playerID);
				});

				SyncGroups(user);
			});

#if RUST
			GetAvatar(player.Id,
				avatar => ImageLibrary?.Call("AddImage", avatar, $"avatar_{player.Id}"));
#endif
		}

		private void OnUserDisconnected(IPlayer player)
		{
			if (player == null) return;

			SendPlayerTime(player.Id, true);

			UnloadPlayerData(player.Id);

			ExecuteReward(new List<string>
			{
				"DISCONNECT"
			}, player.Id);
		}

		private void OnUserRespawned(IPlayer player)
		{
			if (player == null) return;

			ExecuteReward(new List<string>
			{
				"SPAWN"
			}, player.Id);
		}

#if RUST
		private void OnPlayerDeath(BasePlayer player, HitInfo info)
		{
			if (player == null || player.IsNpc) return;

			ExecuteReward(new List<string>
			{
				"DEATH"
			}, player.UserIDString);
		}
#endif

#if SEVENDAYSTODIE
	        private void OnEntityDeath(EntityPlayer player, DamageResponse response)
	        {
	            if (player == null) return;
	
	            var playerDataFromEntityID = global::GameManager.Instance.persistentPlayers.GetPlayerDataFromEntityID(player.entityId);
	            if (playerDataFromEntityID == null) return;
	
	            var userID = playerDataFromEntityID.PrimaryId?.ReadablePlatformUserIdentifier;
	            if (string.IsNullOrEmpty(userID)) return;
	
	            userID = userID.Replace("Steam_", string.Empty);
	
	            ExecuteReward(new List<string>
	            {
	                "DEATH"
	            }, userID);
	        }
#endif

		#endregion

		#region Permissions

		private void OnUserGroupAdded(string id, string groupName)
		{
			var user = GetVyHubUser(id);
			if (user == null) return;
			
			var remove = _groupsBackLog.Remove(GetGroupBacklogKey(id, groupName, GroupOperation.add));
			if (remove == false)
				AddUserToVyHubGroup(user, groupName,
					membership =>
					{
						Log(LogType.INFO,
							$"Added VyHub group membership in group {groupName} for player {user.Username}.");
					});
		}

		private void OnUserGroupRemoved(string id, string groupName)
		{
			var user = GetVyHubUser(id);
			if (user == null) return;

			var remove = _groupsBackLog.Remove(GetGroupBacklogKey(id, groupName, GroupOperation.remove));
			if (remove == false)
				RemoveUserFromVyHubGroup(user, groupName,
					membership =>
					{
						Log(LogType.INFO,
							$"Ended VyHub group membership in group {groupName} for player {user.Username}.");
					});
		}

		#endregion

		#endregion

		#region Commands

		private readonly string
			MSG_EDIT_CONFIG_ERR_SYNTAX = $"Error syntax! Use: /{CMD_EDIT_CONFIG} <api_key/api_url/server_id> <value>",
			MSG_SETUP_CONFIG_ERR_SYNTAX = $"Error syntax! Use: /{CMD_EDIT_CONFIG} <api_key> <api_url> <server_id>",
			MSG_WARN_ERR_SYNTAX = $"Error syntax! Use: /{CMD_WARN} <steam id> <reason>",
			MSG_WARN_NO_PERMISSIONS = "You do not have permission to warn other players!",
			MSG_BAN_NO_PERMISSIONS = "You do not have permission to ban other players!", 
			MSG_UNBAN_NO_PERMISSIONS = "You do not have permission to unban other players!";

		private void CmdEditConfig(IPlayer player, string command, string[] args)
		{
			if (!player.IsServer) return;

			if (args.Length < 2)
			{
				player.Reply(MSG_EDIT_CONFIG_ERR_SYNTAX);
				return;
			}

			var value = string.Join(" ", args.Skip(1));
			if (string.IsNullOrWhiteSpace(value))
			{
				player.Reply(MSG_EDIT_CONFIG_ERR_SYNTAX);
				return;
			}

			var param = args[0];
			switch (param)
			{
				case "api_key":
				{
					_config.API.Key = value;
					break;
				}
				case "api_url":
				{
					_config.API.URL = value;
					break;
				}
				case "server_id":
				{
					_config.API.ServerID = value;
					break;
				}
				default:
				{
					player.Reply(MSG_EDIT_CONFIG_ERR_SYNTAX);
					return;
				}
			}

			player.Reply($"You have successfully set the \"{param}\" parameter to \"{value}\"!");

			_config.API.InitOrUpdate();

			SaveConfig();

			GetServerInformation();
		}

		private void CmdSetupConfig(IPlayer player, string command, string[] args)
		{
			if (!player.IsServer) return;

			if (args.Length < 3)
			{
				player.Reply(MSG_SETUP_CONFIG_ERR_SYNTAX);
				return;
			}

			var api_key = args[0];
			var api_url = args[1];
			var server_id = args[2];
			if (string.IsNullOrWhiteSpace(api_key) || string.IsNullOrWhiteSpace(api_url) ||
			    string.IsNullOrWhiteSpace(server_id))
			{
				player.Reply(MSG_SETUP_CONFIG_ERR_SYNTAX);
				return;
			}

			_config.API = new APISettings
			{
				URL = api_url,
				Key = api_key,
				ServerID = server_id
			};

			_config.API.InitOrUpdate();

			player.Reply(
				$"You have successfully set the following API parameters in the config: URL={api_url}, KEY={api_key}, Server ID={server_id}.");

			SaveConfig();

			GetServerInformation();
		}

		private void CmdWarn(IPlayer player, string command, string[] args)
		{
			if (!player.IsServer && !player.HasPermission($"{Name}.warn"))
			{
				player.Reply(MSG_WARN_NO_PERMISSIONS);
				return;
			}

			if (args.Length < 2)
			{
				player.Reply(MSG_WARN_ERR_SYNTAX);
				return;
			}

			var targetPlayer = covalence.Players.FindPlayerById(args[0]);
			if (targetPlayer == null || !targetPlayer.IsConnected)
			{
				player.Reply(Msg(player, ErrorPlayerOffline));
				return;
			}

			CreateWarning(player, targetPlayer, args[1]);
		}

		#endregion

		#region Dashboard

#if RUST

		#region Fields

		[PluginReference] private Oxide.Core.Plugins.Plugin ImageLibrary = null;

		private enum DashboardTab
		{
			Players = 0
		}

		private Dictionary<ulong, ConfirmData> _confirmPlayers = new Dictionary<ulong, ConfirmData>();

		private class ConfirmData
		{
			public string Reason = string.Empty;

			public string Duration = string.Empty;
		}

		private const string 
			DashboardLayer = "UI.VyHub.Dashboard",
			DashboardMainLayer = "UI.VyHub.Dashboard.Main",
			DashboardConfirmLayer = "UI.VyHub.Dashboard.Confirm",
			DashboardConfirmMainLayer = "UI.VyHub.Dashboard.Confirm.Main", 
			DashboardOpenCmd = "dashboard",
			DashboardConsoleCmd = "UI_VyHub_Dashboard",
			DASHBOARD_ACTION_BAN = "ban",
			DASHBOARD_ACTION_WARN = "warn",
			DASHBOARD_ACTION_UNBAN = "unban";

		private const int 
			DASHBOARD_TABLE_PLAYERS_CONST_X_INDENT = 205,
			DASHBOARD_TABLE_PLAYERS_ON_LINE = 2,
			DASHBOARD_TABLE_PLAYERS_MAX_LINES = 8,
			DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT =
			DASHBOARD_TABLE_PLAYERS_ON_LINE * DASHBOARD_TABLE_PLAYERS_MAX_LINES;

		private const float 
			DASHBOARD_HEADER_BTN_WIDTH = 25,
			DASHBOARD_TAB_HEIGHT = 36,
			DASHBOARD_TAB_MARGIN = 0,
			DASHBOARD_TABLE_PLAYERS_WIDTH = 292f,
			DASHBOARD_TABLE_PLAYERS_AVATAR_SIZE = 36f,
			DASHBOARD_TABLE_PLAYERS_HEIGHT = 36f,
			DASHBOARD_TABLE_PLAYERS_X_MARGIN = 16f,
			DASHBOARD_TABLE_PLAYERS_Y_MARGIN = 8f,
			DASHBOARD_TABLE_PLAYERS_BTN_WIDTH = 65f,
			DASHBOARD_TABLE_PLAYERS_BTN_HEIGHT = 20f,
			DASHBOARD_TABLE_PLAYERS_BTN_MARGIN = 5f,
			DASHBOARD_CONFIRM_HEIGHT = 230f,
			DASHBOARD_CONFIRM_BANS_HEIGHT = 285f;

		#endregion

		#region Colors

		private string _color1;
		private string _color2;
		private string _color3;
		private string _color4;
		private string _color5;
		private string _color6;
		private string _color7;
		private string _color8;
		private string _color9;
		private string _color10;

		private void InitColors()
		{
			_color1 = HexToCuiColor("#0E0E10");
			_color2 = HexToCuiColor("#161617");
			_color3 = HexToCuiColor("#FFFFFF");
			_color4 = HexToCuiColor("#4B68FF");
			_color5 = HexToCuiColor("#4B68FF", 50);
			_color6 = HexToCuiColor("#242425");
			_color7 = HexToCuiColor("#000000");
			_color8 = HexToCuiColor("#FFFFFF", 50);
			_color9 = HexToCuiColor("#FF4B4B");
			_color10 = HexToCuiColor("#B19F56");
		}

		private static string HexToCuiColor(string HEX, float Alpha = 100)
		{
			if (string.IsNullOrEmpty(HEX)) HEX = "#FFFFFF";

			var str = HEX.Trim('#');
			if (str.Length != 6) throw new Exception(HEX);
			var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
			var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
			var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);

			return $"{(double) r / 255} {(double) g / 255} {(double) b / 255} {Alpha / 100f}";
		}

		#endregion

		#region Interface

		private void DashboardUI(BasePlayer player, DashboardTab selectedTab = DashboardTab.Players, int page = 0,
			string search = "", bool first = false)
		{
			var container = new Oxide.Game.Rust.Cui.CuiElementContainer();

			float xSwitch;
			float ySwitch;
			float localSwitch;

			#region Background

			if (first)
			{
				container.Add(new Oxide.Game.Rust.Cui.CuiPanel
				{
					RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
					Image =
					{
						Color = "0 0 0 0.9",
						Material = "assets/content/ui/uibackgroundblur.mat"
					},
					CursorEnabled = true
				}, "Overlay", DashboardLayer + ".Background", DashboardLayer + ".Background");

				container.Add(new Oxide.Game.Rust.Cui.CuiButton
				{
					RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
					Text = {Text = ""},
					Button =
					{
						Color = "0 0 0 0",
						Close = DashboardLayer + ".Background"
					}
				}, DashboardLayer + ".Background");

				container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
				{
					RectTransform =
					{
						AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5",
						OffsetMin = "-410 -210",
						OffsetMax = "410 210"
					},
					Image =
					{
						Color = _color1
					}
				}, DashboardLayer + ".Background", DashboardLayer);
			}

			#endregion

			#region Main

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
				Image =
				{
					Color = "0 0 0 0"
				}
			}, DashboardLayer, DashboardMainLayer, DashboardMainLayer);

			#endregion

			#region Header

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel
			{
				RectTransform =
				{
					AnchorMin = "0 1", AnchorMax = "1 1",
					OffsetMin = "0 -50",
					OffsetMax = "0 0"
				},
				Image = {Color = _color2}
			}, DashboardMainLayer, DashboardMainLayer + ".Header");

			container.Add(new Oxide.Game.Rust.Cui.CuiLabel
			{
				RectTransform =
				{
					AnchorMin = "0 0", AnchorMax = "1 1",
					OffsetMin = "30 0",
					OffsetMax = "0 0"
				},
				Text =
				{
					Text = "Dashboard",
					Align = TextAnchor.MiddleLeft,
					Font = "robotocondensed-bold.ttf",
					FontSize = 14,
					Color = _color3
				}
			}, DashboardMainLayer + ".Header");

			xSwitch = -25f;

			#region Close

			container.Add(new Oxide.Game.Rust.Cui.CuiButton
			{
				RectTransform =
				{
					AnchorMin = "1 1", AnchorMax = "1 1",
					OffsetMin = $"{xSwitch - DASHBOARD_HEADER_BTN_WIDTH} -37.5",
					OffsetMax = $"{xSwitch} -12.5"
				},
				Text =
				{
					Text = "✕",
					Align = TextAnchor.MiddleCenter,
					Font = "robotocondensed-bold.ttf",
					FontSize = 10,
					Color = _color3
				},
				Button =
				{
					Close = DashboardLayer + ".Background",
					Color = _color4
				}
			}, DashboardMainLayer + ".Header");

			#endregion

			#endregion

			#region Tabs

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform =
				{
					AnchorMin = "0 0", AnchorMax = "0 1",
					OffsetMin = "15 15",
					OffsetMax = "185 -65"
				},
				Image =
				{
					Color = _color2
				}
			}, DashboardMainLayer, DashboardMainLayer + ".Tabs");

			ySwitch = 0f;

			foreach (DashboardTab targetTab in Enum.GetValues(typeof(DashboardTab)))
			{
				var isSelectedTab = targetTab == selectedTab;

				if (isSelectedTab)
				{
					container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
						{
							RectTransform =
							{
								AnchorMin = "0 1", AnchorMax = "1 1",
								OffsetMin = $"0 {ySwitch - DASHBOARD_TAB_HEIGHT}",
								OffsetMax = $"0 {ySwitch}"
							},
							Image =
							{
								Color = _color5
							}
						}, DashboardMainLayer + ".Tabs", DashboardMainLayer + $".Tab.{targetTab}");

					container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
						{
							RectTransform =
							{
								AnchorMin = "0 0", AnchorMax = "0 1",
								OffsetMin = "0 0",
								OffsetMax = "5 0"
							},
							Image =
							{
								Color = _color4
							}
						}, DashboardMainLayer + $".Tab.{targetTab}");

					container.Add(new Oxide.Game.Rust.Cui.CuiButton()
						{
							RectTransform =
							{
								AnchorMin = "0 0",
								AnchorMax = "1 1",
								OffsetMin = "0 0",
								OffsetMax = "0 0"
							},
							Text =
							{
								Text = $"{targetTab.ToString()}",
								Align = TextAnchor.MiddleCenter,
								Font = "robotocondensed-regular.ttf",
								Color = _color3
							},
							Button =
							{
								Color = "0 0 0 0",
								Command = $"{DashboardConsoleCmd} page {targetTab} 0 {search}"
							}
						}, DashboardMainLayer + $".Tab.{targetTab}");
				}
				else
				{
					container.Add(new Oxide.Game.Rust.Cui.CuiButton()
						{
							RectTransform =
							{
								AnchorMin = "0 1", AnchorMax = "1 1",
								OffsetMin = $"0 {ySwitch - DASHBOARD_TAB_HEIGHT}",
								OffsetMax = $"0 {ySwitch}"
							},
							Text =
							{
								Text = $"{targetTab.ToString()}",
								Align = TextAnchor.MiddleCenter,
								Font = "robotocondensed-regular.ttf"
							},
							Button =
							{
								Color = "0 0 0 0",
								Command = $"{DashboardConsoleCmd} page {targetTab} 0 {search}"
							}
						}, DashboardMainLayer + ".Tabs", DashboardMainLayer + $".Tab.{targetTab}");
				}

				ySwitch = ySwitch - DASHBOARD_TAB_HEIGHT - DASHBOARD_TAB_MARGIN;
			}

			#endregion

			#region Selected Tab

			switch (selectedTab)
			{
				case DashboardTab.Players:
					var members = GetPlayers(page, search);

					#region Header

					xSwitch = xSwitch - DASHBOARD_HEADER_BTN_WIDTH - 5f;

					#region Next Page

					container.Add(new Oxide.Game.Rust.Cui.CuiButton
					{
						RectTransform =
						{
							AnchorMin = "1 1", AnchorMax = "1 1",
							OffsetMin = $"{xSwitch - DASHBOARD_HEADER_BTN_WIDTH} -37.5",
							OffsetMax = $"{xSwitch} -12.5"
						},
						Text =
						{
							Text = "▶",
							Align = TextAnchor.MiddleCenter,
							Font = "robotocondensed-bold.ttf",
							FontSize = 10,
							Color = _color3
						},
						Button =
						{
							Color = _color6,
							Command = members.Length > (page + 1) * DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT
								? $"{DashboardConsoleCmd} page {selectedTab} {page + 1} {search}"
								: ""
						}
					}, DashboardMainLayer + ".Header");

					#endregion

					xSwitch = xSwitch - DASHBOARD_HEADER_BTN_WIDTH - 5f;

					#region Back Page

					container.Add(new Oxide.Game.Rust.Cui.CuiButton
					{
						RectTransform =
						{
							AnchorMin = "1 1", AnchorMax = "1 1",
							OffsetMin = $"{xSwitch - DASHBOARD_HEADER_BTN_WIDTH} -37.5",
							OffsetMax = $"{xSwitch} -12.5"
						},
						Text =
						{
							Text = "◀",
							Align = TextAnchor.MiddleCenter,
							Font = "robotocondensed-bold.ttf",
							FontSize = 10,
							Color = _color3
						},
						Button =
						{
							Color = _color6,
							Command = page != 0
								? $"{DashboardConsoleCmd} page {selectedTab} {page - 1} {search}"
								: ""
						}
					}, DashboardMainLayer + ".Header");

					#endregion

					xSwitch = xSwitch - DASHBOARD_HEADER_BTN_WIDTH - 5f;

					#region Search

					container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
					{
						RectTransform =
						{
							AnchorMin = "1 1", AnchorMax = "1 1",
							OffsetMin = $"{xSwitch - 140} -37.5",
							OffsetMax = $"{xSwitch} -12.5"
						},
						Image =
						{
							Color = _color7
						}
					}, DashboardMainLayer + ".Header", DashboardMainLayer + ".Header.Search");

					container.Add(new Oxide.Game.Rust.Cui.CuiElement()
					{
						Parent = DashboardMainLayer + ".Header.Search",
						Components =
						{
							new Oxide.Game.Rust.Cui.CuiInputFieldComponent()
							{
								FontSize = 12,
								Align = TextAnchor.MiddleLeft,
								Command = $"{DashboardConsoleCmd} page {selectedTab} {page}",
								Color = "1 1 1 0.95",
								CharsLimit = 150,
								Text = !string.IsNullOrWhiteSpace(search) ? $"{search}" : "Search...",
								NeedsKeyboard = true
							},
							new Oxide.Game.Rust.Cui.CuiRectTransformComponent()
							{
								AnchorMin = "0 0", AnchorMax = "1 1",
								OffsetMin = "5 0", OffsetMax = "-5 0"
							}
						}
					});

					if (HasSearch(search))
						container.Add(new Oxide.Game.Rust.Cui.CuiButton()
						{
							RectTransform =
							{
								AnchorMin = "1 0.5", AnchorMax = "1 0.5",
								OffsetMin = "-25 -12.5",
								OffsetMax = "0 12.5"
							},
							Text =
							{
								Text = "✕",
								Align = TextAnchor.MiddleCenter,
								Font = "robotocondensed-regular.ttf",
								FontSize = 10,
								Color = _color8
							},
							Button =
							{
								Color = "0 0 0 0",
								Command = $"{DashboardConsoleCmd} page {selectedTab}"
							}
						}, DashboardMainLayer + ".Header.Search");

					#endregion

					#endregion

					#region Content

					ySwitch = -70f;
					xSwitch = DASHBOARD_TABLE_PLAYERS_CONST_X_INDENT;

					for (var index = 0; index < members.Length; index++)
					{
						var member = members[index];
						var isWarned = _warnedUsers.Contains(member.UserIDString);
						var isBanned = server.IsBanned(player.UserIDString) || _cachedBans.Contains(member.UserIDString);

						#region Background

						container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
							{
								RectTransform =
								{
									AnchorMin = "0 1", AnchorMax = "0 1",
									OffsetMin = $"{xSwitch} {ySwitch - DASHBOARD_TABLE_PLAYERS_HEIGHT}",
									OffsetMax = $"{xSwitch + DASHBOARD_TABLE_PLAYERS_WIDTH} {ySwitch}"
								},
								Image =
								{
									Color = _color2
								}
							}, DashboardMainLayer, DashboardMainLayer + $".Content.Player.{index}");

						#endregion

						#region Avatar

						container.Add(new Oxide.Game.Rust.Cui.CuiElement()
						{
							Parent = DashboardMainLayer + $".Content.Player.{index}",
							Components =
							{
								new Oxide.Game.Rust.Cui.CuiRawImageComponent
								{
									Png = ImageLibrary.Call<string>("GetImage", $"avatar_{member.UserIDString}")
								},
								new Oxide.Game.Rust.Cui.CuiRectTransformComponent
								{
									AnchorMin = "0 0", AnchorMax = "0 1",
									OffsetMin = "0 0",
									OffsetMax = $"{DASHBOARD_TABLE_PLAYERS_AVATAR_SIZE} 0"
								}
							}
						});

						#endregion

						#region Name&SteamID

						container.Add(new Oxide.Game.Rust.Cui.CuiLabel()
							{
								RectTransform =
								{
									AnchorMin = "0 0.5", AnchorMax = "1 1",
									OffsetMin = $"{DASHBOARD_TABLE_PLAYERS_AVATAR_SIZE + 10} 0",
									OffsetMax = "0 0"
								},
								Text =
								{
									Text = $"{member.displayName}",
									Align = TextAnchor.LowerLeft,
									Font = "robotocondensed-bold.ttf",
									FontSize = 12,
									Color = _color3
								}
							}, DashboardMainLayer + $".Content.Player.{index}");

						container.Add(new Oxide.Game.Rust.Cui.CuiLabel()
							{
								RectTransform =
								{
									AnchorMin = "0 0", AnchorMax = "1 0.5",
									OffsetMin = $"{DASHBOARD_TABLE_PLAYERS_AVATAR_SIZE + 10} 0",
									OffsetMax = "0 0"
								},
								Text =
								{
									Text = $"{member.UserIDString}",
									Align = TextAnchor.UpperLeft,
									Font = "robotocondensed-regular.ttf",
									FontSize = 10,
									Color = _color3
								}
							}, DashboardMainLayer + $".Content.Player.{index}");

						#endregion

						#region Buttons

						localSwitch = -5;

						container.Add(new Oxide.Game.Rust.Cui.CuiButton()
							{
								RectTransform =
								{
									AnchorMin = "1 0.5", AnchorMax = "1 0.5",
									OffsetMin =
										$"{localSwitch - DASHBOARD_TABLE_PLAYERS_BTN_WIDTH} -{DASHBOARD_TABLE_PLAYERS_BTN_HEIGHT / 2}",
									OffsetMax = $"{localSwitch} {DASHBOARD_TABLE_PLAYERS_BTN_HEIGHT / 2}"
								},
								Text =
								{
									Text = isBanned ? "Unban" : "Ban",
									Align = TextAnchor.MiddleCenter,
									Font = "robotocondensed-regular.ttf",
									FontSize = 10,
									Color = _color3
								},
								Button =
								{
									Command = 
										isBanned ?
										$"{DashboardConsoleCmd} {DASHBOARD_ACTION_UNBAN} {member.UserIDString}"
										: $"{DashboardConsoleCmd} {DASHBOARD_ACTION_BAN} {member.UserIDString}",
									Color = _color9
								}
							}, DashboardMainLayer + $".Content.Player.{index}");

						localSwitch = localSwitch - DASHBOARD_TABLE_PLAYERS_BTN_WIDTH -
						              DASHBOARD_TABLE_PLAYERS_BTN_MARGIN;

						container.Add(new Oxide.Game.Rust.Cui.CuiButton()
							{
								RectTransform =
								{
									AnchorMin = "1 0.5", AnchorMax = "1 0.5",
									OffsetMin =
										$"{localSwitch - DASHBOARD_TABLE_PLAYERS_BTN_WIDTH} -{DASHBOARD_TABLE_PLAYERS_BTN_HEIGHT / 2}",
									OffsetMax = $"{localSwitch} {DASHBOARD_TABLE_PLAYERS_BTN_HEIGHT / 2}"
								},
								Text =
								{
									Text = isWarned ? "Has warning" : "Warn",
									Align = TextAnchor.MiddleCenter,
									Font = "robotocondensed-regular.ttf",
									FontSize = 10,
									Color = _color3
								},
								Button =
								{
									Command = 
										isWarned 
											? ""
										: $"{DashboardConsoleCmd} {DASHBOARD_ACTION_WARN} {member.UserIDString}",
									Color = isWarned ? _color10 : _color4
								}
							}, DashboardMainLayer + $".Content.Player.{index}");

						#endregion

						#region Table Utils

						if ((index + 1) % DASHBOARD_TABLE_PLAYERS_ON_LINE == 0)
						{
							ySwitch = ySwitch - DASHBOARD_TABLE_PLAYERS_HEIGHT - DASHBOARD_TABLE_PLAYERS_Y_MARGIN;
							xSwitch = DASHBOARD_TABLE_PLAYERS_CONST_X_INDENT;
						}
						else
						{
							xSwitch += DASHBOARD_TABLE_PLAYERS_WIDTH + DASHBOARD_TABLE_PLAYERS_X_MARGIN;
						}

						#endregion
					}

					#endregion

					break;
			}

			#endregion

			Oxide.Game.Rust.Cui.CuiHelper.AddUi(player, container);
		}

		private void ConfirmActionUI(BasePlayer player, string action, string targetName, ulong targetID)
		{
			var container = new Oxide.Game.Rust.Cui.CuiElementContainer();

			var confirm = GetOrAddConfirmData(player);

			float ySwitch;
			float height;

			#region Background

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
				Image =
				{
					Color = "0 0 0 0.9",
					Material = "assets/content/ui/uibackgroundblur.mat"
				},
				CursorEnabled = true
			}, "Overlay", DashboardConfirmLayer, DashboardConfirmLayer);

			container.Add(new Oxide.Game.Rust.Cui.CuiButton
			{
				RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
				Text = {Text = ""},
				Button =
				{
					Color = "0 0 0 0",
					Close = DashboardConfirmLayer,
					Command = $"{DashboardConsoleCmd} confirm cancel"
				}
			}, DashboardConfirmLayer);

			#endregion

			#region Main

			var mainHeight = action == DASHBOARD_ACTION_BAN ? DASHBOARD_CONFIRM_BANS_HEIGHT : DASHBOARD_CONFIRM_HEIGHT;

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform =
				{
					AnchorMin = "0.5 0.5",
					AnchorMax = "0.5 0.5",
					OffsetMin = $"-125 -{mainHeight / 2f}",
					OffsetMax = $"125 {mainHeight / 2f}"
				},
				Image =
				{
					Color = _color1
				}
			}, DashboardConfirmLayer, DashboardConfirmMainLayer, DashboardConfirmMainLayer);

			#endregion

			#region Header

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform =
				{
					AnchorMin = "0 1", AnchorMax = "1 1",
					OffsetMin = "0 -50", OffsetMax = "0 0"
				},
				Image =
				{
					Color = _color2
				}
			}, DashboardConfirmMainLayer, DashboardConfirmMainLayer + ".Header");

			container.Add(new Oxide.Game.Rust.Cui.CuiLabel
			{
				RectTransform =
				{
					AnchorMin = "0 0", AnchorMax = "1 1",
					OffsetMin = "20 0",
					OffsetMax = "0 0"
				},
				Text =
				{
					Text = "Confirm Action",
					Align = TextAnchor.MiddleLeft,
					Font = "robotocondensed-bold.ttf",
					FontSize = 14,
					Color = _color3
				}
			}, DashboardConfirmMainLayer + ".Header");

			container.Add(new Oxide.Game.Rust.Cui.CuiButton
			{
				RectTransform =
				{
					AnchorMin = "1 1", AnchorMax = "1 1",
					OffsetMin = "-35 -37.5",
					OffsetMax = "-10 -12.5"
				},
				Text =
				{
					Text = "✕",
					Align = TextAnchor.MiddleCenter,
					Font = "robotocondensed-bold.ttf",
					FontSize = 10,
					Color = _color3
				},
				Button =
				{
					Close = DashboardConfirmLayer,
					Color = _color4,
					Command = $"{DashboardConsoleCmd} confirm cancel"
				}
			}, DashboardConfirmMainLayer + ".Header");

			#endregion

			ySwitch = -60f;
			height = 50f;

			#region Message

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
			{
				RectTransform =
				{
					AnchorMin = "0 1",
					AnchorMax = "1 1",
					OffsetMin = $"20 {ySwitch - height}",
					OffsetMax = $"-20 {ySwitch}"
				},
				Image =
				{
					Color = _color2
				}
			}, DashboardConfirmMainLayer, DashboardConfirmMainLayer + ".Message");

			container.Add(new Oxide.Game.Rust.Cui.CuiLabel()
			{
				RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
				Text =
				{
					Text = $"Do you really want to <b>{action}</b> player\n<b>{targetName}</b> (<b>{targetID}</b>)?",
					Align = TextAnchor.MiddleCenter,
					Font = "robotocondensed-regular.ttf",
					FontSize = 12,
					Color = _color3
				}
			}, DashboardConfirmMainLayer + ".Message");

			#endregion

			if (action != DASHBOARD_ACTION_UNBAN)
			{
				#region Reason

				ySwitch = ySwitch - height - 5f;

				EnterFieldUI(ref container, ref ySwitch, ref height, confirm, "reason");

				#endregion

				#region Duration

				if (action == DASHBOARD_ACTION_BAN)
				{
					ySwitch = ySwitch - height - 5f;

					EnterFieldUI(ref container, ref ySwitch, ref height, confirm, "duration");
				}

				#endregion
			}
			
			#region Buttons

			ySwitch = ySwitch - height - 10f;
			height = 40;

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel
			{
				RectTransform =
				{
					AnchorMin = "0 1", AnchorMax = "1 1",
					OffsetMin = $"20 {ySwitch - height}", OffsetMax = $"-20 {ySwitch}"
				},
				Image =
				{
					Color = _color2
				}
			}, DashboardConfirmMainLayer, DashboardConfirmMainLayer + ".Buttons");

			#region Cancel

			container.Add(new Oxide.Game.Rust.Cui.CuiButton()
			{
				RectTransform =
				{
					AnchorMin = "0 0.5", AnchorMax = "0 0.5",
					OffsetMin = "15 -12.5",
					OffsetMax = "95 12.5"
				},
				Text =
				{
					Text = "Cancel",
					Align = TextAnchor.MiddleCenter,
					Font = "robotocondensed-regular.ttf",
					Color = _color3
				},
				Button =
				{
					Close = DashboardConfirmLayer,
					Color = _color4,
					Command = $"{DashboardConsoleCmd} confirm cancel"
				}
			}, DashboardConfirmMainLayer + ".Buttons");

			#endregion

			#region Accept

			container.Add(new Oxide.Game.Rust.Cui.CuiButton()
			{
				RectTransform =
				{
					AnchorMin = "1 0.5", AnchorMax = "1 0.5",
					OffsetMin = "-95 -12.5",
					OffsetMax = "-15 12.5"
				},
				Text =
				{
					Text = "Accept",
					Align = TextAnchor.MiddleCenter,
					Font = "robotocondensed-regular.ttf",
					Color = _color3
				},
				Button =
				{
					Close = DashboardConfirmLayer,
					Color = _color9,
					Command = $"{DashboardConsoleCmd} confirm {action} {targetID}"
				}
			}, DashboardConfirmMainLayer + ".Buttons");

			#endregion

			#endregion

			Oxide.Game.Rust.Cui.CuiHelper.AddUi(player, container);
		}

		private void EnterFieldUI(ref Oxide.Game.Rust.Cui.CuiElementContainer container, ref float ySwitch, ref float height,
			ConfirmData confirm, string type)
		{
			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
				{
					RectTransform =
					{
						AnchorMin = "0 1",
						AnchorMax = "1 1",
						OffsetMin = $"20 {ySwitch - height}",
						OffsetMax = $"-20 {ySwitch}"
					},
					Image =
					{
						Color = _color2
					}
				}, DashboardConfirmMainLayer, DashboardConfirmMainLayer + $".{type}");

			string title;
			switch (type)
			{
				case "duration":
					title = $"Enter a {type} (in seconds):";
					break;
				default:
					title = $"Enter a {type}:";
					break;
			}

			container.Add(new Oxide.Game.Rust.Cui.CuiLabel()
				{
					RectTransform =
					{
						AnchorMin = "0 1", AnchorMax = "1 1",
						OffsetMin = "0 -20",
						OffsetMax = "0 0"
					},
					Text =
					{
						Text = $"{title}",
						Align = TextAnchor.MiddleCenter,
						Font = "robotocondensed-regular.ttf",
						FontSize = 12,
						Color = _color3
					}
				}, DashboardConfirmMainLayer + $".{type}");

			container.Add(new Oxide.Game.Rust.Cui.CuiPanel()
				{
					RectTransform =
					{
						AnchorMin = "0 0", AnchorMax = "1 0",
						OffsetMin = "5 5",
						OffsetMax = "-5 30"
					},
					Image =
					{
						Color = _color1
					}
				}, DashboardConfirmMainLayer + $".{type}", DashboardConfirmMainLayer + $".{type}.Enter");

			var param = type == "reason" ? confirm.Reason : confirm.Duration;

			container.Add(new Oxide.Game.Rust.Cui.CuiElement
			{
				Parent = DashboardConfirmMainLayer + $".{type}.Enter",
				Components =
				{
					new Oxide.Game.Rust.Cui.CuiInputFieldComponent
					{
						FontSize = 10,
						Align = TextAnchor.MiddleLeft,
						Command = $"{DashboardConsoleCmd} confirm enter {type}",
						Color = "1 1 1 0.95",
						CharsLimit = 150,
						Text = !string.IsNullOrWhiteSpace(param) ? $"{param}" : string.Empty,
						NeedsKeyboard = true
					},
					new Oxide.Game.Rust.Cui.CuiRectTransformComponent
					{
						AnchorMin = "0 0", AnchorMax = "1 1",
						OffsetMin = "10 0", OffsetMax = "0 0"
					}
				}
			});
		}

		#endregion

		#region Commands

		private void CmdOpenDashboard(IPlayer cov, string command, string[] args)
		{
			if (!cov.HasPermission($"{Name}.dashboard"))
				return;
			
			var player = cov.Object as BasePlayer;
			if (player == null) return;

			DashboardUI(player, first: true);
		}

		private void CmdConsoleDashboard(IPlayer covPlayer, string command, string[] args)
		{
			var player = covPlayer?.Object as BasePlayer;
			if (player == null || args.Length < 1 || 
			    !permission.UserHasPermission(player.UserIDString, $"{Name}.dashboard"))
				return;
			
			switch (args[0])
			{
				case "page":
				{
					DashboardTab targetTab;
					if (!(args.Length >= 2) || !Enum.TryParse(args[1], out targetTab)) return;

					var page = 0;
					if (args.Length >= 3)
						int.TryParse(args[2], out page);

					var search = args.Length >= 4 ? args[3] : string.Empty;
					if (search == "delete")
						search = string.Empty;

					DashboardUI(player, targetTab, page, search);
					break;
				}

				case DASHBOARD_ACTION_BAN:
				case DASHBOARD_ACTION_WARN:
				{
					ulong targetID;
					if (!(args.Length >= 2) || !ulong.TryParse(args[1], out targetID)) return;

					string permissionMsg;
					if (!HasActionPermission(player.UserIDString, args[0], out permissionMsg))
					{
						player.ChatMessage(permissionMsg);
						return;
					}
					
					if (player.userID == targetID)
					{
						player.ChatMessage($"You can't {args[0]} yourself!");
						return;
					}

					var targetPlayer = covalence.Players.FindPlayerById(targetID.ToString());
					if (targetPlayer == null) return;

					ConfirmActionUI(player, args[0], targetPlayer.Name, targetID);
					break;
				}

				case DASHBOARD_ACTION_UNBAN:
				{
					ulong targetID;
					if (!(args.Length >= 2) || !ulong.TryParse(args[1], out targetID)) return;

					string permissionMsg;
					if (!HasActionPermission(player.UserIDString, args[0], out permissionMsg))
					{
						player.ChatMessage(permissionMsg);
						return;
					}
					
					if (player.userID == targetID)
					{
						player.ChatMessage($"You can't {args[0]} yourself!");
						return;
					}

					var targetPlayer = covalence.Players.FindPlayerById(targetID.ToString());
					if (targetPlayer == null) return;

					ConfirmActionUI(player, args[0], targetPlayer.Name, targetID);
					break;
				}

				case "confirm":
				{
					if (!(args.Length >= 2)) return;

					var action = args[1];

					switch (action)
					{
						case DASHBOARD_ACTION_WARN:
						case DASHBOARD_ACTION_BAN:
						case DASHBOARD_ACTION_UNBAN:
						{
							string permissionMsg;
							if (!HasActionPermission(player.UserIDString, action, out permissionMsg))
							{
								player.ChatMessage(permissionMsg);
								return;
							}
							break;
						}
					}
					
					switch (action)
					{
						case DASHBOARD_ACTION_BAN:
						case DASHBOARD_ACTION_WARN:
						{
							if (!(args.Length >= 3)) return;

							var targetID = args[2];
							if (string.IsNullOrWhiteSpace(targetID) || !targetID.IsSteamId())
								return;

							ConfirmData confirmData;
							if (!_confirmPlayers.TryGetValue(player.userID, out confirmData))
								return;

							switch (action)
							{
								case DASHBOARD_ACTION_BAN:
								{
									var duration = default(TimeSpan);
									if (!string.IsNullOrEmpty(confirmData.Duration))
									{
										var seconds = Convert.ToDouble(confirmData.Duration);
										if (seconds > 0) 
											duration = TimeSpan.FromSeconds(seconds);
									}
									
									if (string.IsNullOrEmpty(confirmData.Reason)) 
										confirmData.Reason = "no reason";

									var targetPlayer = covalence.Players.FindPlayerById(targetID);
									if (targetPlayer != null)
									{
										_cachedBans.Add(targetID);

										var finalTime = duration.TotalSeconds > 0
											? (long?) duration.TotalSeconds
											: null;

										var adminUser = GetVyHubUser(player.UserIDString);
										if (adminUser != null)
										{
											CreateVyHubBan(finalTime, confirmData.Reason,
												targetID, DateTime.UtcNow, adminUser.ID,
												ban => targetPlayer.Ban(confirmData.Reason, duration));
										}
										else
										{
											CreateVyHubBanWithoutCreator(finalTime, confirmData.Reason,
												targetID, DateTime.UtcNow,
												ban => targetPlayer.Ban(confirmData.Reason, duration));
										}
									}

									break;
								}
								
								case DASHBOARD_ACTION_WARN:
								{
									_warnedUsers.Add(targetID);

									NextTick(() => CreateWarning(player.IPlayer, covalence.Players.FindPlayerById(targetID), confirmData.Reason));
									break;
								}
							}

							_confirmPlayers.Remove(player.userID);

							DashboardUI(player);
							break;
						}

						case DASHBOARD_ACTION_UNBAN:
						{
							if (!(args.Length >= 3)) return;

							var targetID = args[2];
							if (string.IsNullOrWhiteSpace(targetID) || !targetID.IsSteamId())
								return;

							ConfirmData confirmData;
							if (!_confirmPlayers.TryGetValue(player.userID, out confirmData))
								return;

							switch (action)
							{
								case DASHBOARD_ACTION_UNBAN:
								{
									server.Unban(targetID);
									_cachedBans.Remove(targetID);
									break;
								}
							}

							_confirmPlayers.Remove(player.userID);

							DashboardUI(player);
							break;
						}
						
						case "enter":
						{
							if (!(args.Length >= 4)) return;

							var confirmData = GetOrAddConfirmData(player);

							var result = args[3] == "delete" ? string.Empty : string.Join(" ", args.Skip(3));

							switch (args[2])
							{
								case "reason":
								{
									confirmData.Reason = result;
									break;
								}
								
								case "duration":
								{
									result = result.Replace(" ", string.Empty);
									
									double durationTime;
									if (double.TryParse(result, out durationTime))
										confirmData.Duration = result;
									break;
								}
							}
							break;
						}
					}

					break;
				}
			}
		}

		#endregion

		#region Utils

		#region Avatar
		
		private readonly System.Text.RegularExpressions.Regex Regex = new System.Text.RegularExpressions.Regex(@"<avatarFull><!\[CDATA\[(.*)\]\]></avatarFull>");

		private void GetAvatar(string userId, Action<string> callback)
		{
			if (callback == null) return;

			SendWebRequest($"http://steamcommunity.com/profiles/{userId}?xml=1", null, (code, response) =>
			{
				if (code != 200 || response == null)
					return;

				var avatar = Regex?.Match(response).Groups[1].ToString();
				if (string.IsNullOrEmpty(avatar))
					return;

				callback.Invoke(avatar);
			});
		}

		#endregion
		
		private bool HasActionPermission(string playerID, string action, out string resultMessage)
		{
			resultMessage = string.Empty;
 
			string permToCheck;
			
			switch (action)
			{
				case DASHBOARD_ACTION_BAN:
					permToCheck = $"{Name}.ban";

					resultMessage = MSG_BAN_NO_PERMISSIONS;
					break;
				
				case DASHBOARD_ACTION_WARN:
					permToCheck = $"{Name}.warn";

					resultMessage = MSG_WARN_NO_PERMISSIONS;
					break;
				
				case DASHBOARD_ACTION_UNBAN:
					permToCheck = $"{Name}.unban";

					resultMessage = MSG_UNBAN_NO_PERMISSIONS;
					break;
				
				default:
					return true;
			}
			
			return permission.UserHasPermission(playerID, permToCheck);
		}
		
		private void UnloadDashboardUIs()
		{
			for (var i = 0; i < BasePlayer.activePlayerList.Count; i++)
				UnloadDashboardUI(BasePlayer.activePlayerList[i]);
		}

		private void UnloadDashboardUI(BasePlayer player)
		{
			Oxide.Game.Rust.Cui.CuiHelper.DestroyUi(player, DashboardLayer + ".Background");
			Oxide.Game.Rust.Cui.CuiHelper.DestroyUi(player, DashboardConfirmLayer);
		}

		private ConfirmData GetOrAddConfirmData(BasePlayer player)
		{
			ConfirmData confirmData;
			if (!_confirmPlayers.TryGetValue(player.userID, out confirmData))
				_confirmPlayers.TryAdd(player.userID, confirmData = new ConfirmData());
			return confirmData;
		}

		private BasePlayer[] GetPlayers(int page, string search)
		{
			return HasSearch(search) ? BasePlayer.activePlayerList.Where(p => p.UserIDString == search || p.displayName == search ||
			                                              p.displayName.StartsWith(search) || p.displayName.Contains(search) ||
			                                              (search.IsSteamId() && p.UserIDString.Contains(search))).Skip(page * DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT).Take(DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT).ToArray()
				: BasePlayer.activePlayerList.Skip(page * DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT).Take(DASHBOARD_TABLE_PLAYERS_TOTAL_AMOUNT).ToArray();
		}

		private bool HasSearch(string search)
		{
			return !string.IsNullOrWhiteSpace(search) && search.Length > 1;
		}

		#endregion

#endif

		#endregion

		#region API Client

		#region Server Info

		private void GetServerInformation(Action callback = null)
		{
			SendWebRequestAndFetch(_config.API.ServerEndpoint, null,
				"Cannot connect to VyHub API! Please follow the installation instructions: {0}",
				"ServerBundle ID",
				new Action<ServerInfo>(serverInfo =>
				{
					_serverBundleID = serverInfo.ServerBundleID;

					callback?.Invoke();

					Puts("Successfully connected to VyHub API.");
				}));
		}

		private void PatchServer(List<Dictionary<string, object>> userActivities)
		{
			SendWebRequestAndFetch(_config.API.ServerEndpoint,
				new Dictionary<string, object>
				{
					["users_max"] = covalence.Server.MaxPlayers,
					["users_current"] = covalence.Server.Players,
					["user_activities"] = userActivities,
					["is_alive"] = true
				},
				"Failed to patch server: {0}",
				"ServerBundle ID",
				new Action<ServerInfo>(serverInfo => { _serverBundleID = serverInfo.ServerBundleID; }), 
				RequestMethod.PATCH);
		}

		private class ServerInfo
		{
			[JsonProperty("serverbundle_id")]
			public string ServerBundleID;
		}

		#endregion

		#region Adverts

		private void FetchAdverts(Action<List<Advert>> callback = null)
		{
			SendWebRequestAndFetch(_config.API.FetchAdvertsEndpoint + $"&serverbundle_id={_serverBundleID}", null,
				"Adverts could not be fetched from VyHub API: {0}",
				"Adverts",
				callback);
		}

		private class Advert
		{
			[JsonProperty("id")] public string ID;

			[JsonProperty("title")] public string Title;

			[JsonProperty("content")]
			public string Content;

			[JsonProperty("color")] public string Color;
		}

		#endregion

		#region User

		private void GetOrCreateUser(string playerID, Action<VyHubUser> callback = null)
		{
			var cachedUser = GetVyHubUser(playerID);
			if (cachedUser != null)
			{
				callback?.Invoke(cachedUser);
				return;
			}

			SendWebRequestAndFetch(_config.API.UserEndpoint + $"{playerID}?type={GameType}", null,
				null,
				"VyHub User",
				new Action<VyHubUser>(user =>
				{
					_vyHubUsers.TryAdd(playerID, user);
					
					callback?.Invoke(user);
				}), onResponse: (response, code) =>
				{
					if (response == null || code != 200)
					{
						if (code == 404)
						{
							CreateUser(playerID, callback);
							return true;
						}

						PrintError($"Failed to get user {playerID} from VyHub API.");
						return true;
					}
					
					return false;
				});
		}

		private void CreateUser(
			string playerID,
			Action<VyHubUser> callback = null)
		{
			var body = new Dictionary<string, object>
			{
				["type"] = GameType,
				["identifier"] = playerID
			};

			var player = covalence.Players.FindPlayerById(playerID);
			if (player != null && !string.IsNullOrEmpty(player.Name)) 
				body.TryAdd("username", player.Name);

			SendWebRequestAndFetch(_config.API.UserEndpoint,
				body,
				"Failed to create user in VyHub API: {0}",
				"VyHub User",
				new Action<VyHubUser>(user =>
				{
					if (user != null)
			
// ... [Code preview truncated due to length. Full source in attached file] ...

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