C#:
using UnityEngine;
using System;
using Oxide.Game.Rust.Cui;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using System.Linq;
using Newtonsoft.Json.Linq;
using Rust;
using System.Text;
using System.Collections;
using UnityEngine.Networking;
using Oxide.Core.Libraries;
using System.Text.RegularExpressions;
using Facepunch;
using Oxide.Plugins.XDStatisticsExtensionMethods;
using Rust.Ai.Gen2;
using UnityEngine.UI;
namespace Oxide.Plugins {
[Info("XDStatistics", "DezLife", "3.2.8")]
[Description("Multifunctional statistics for your server!")]
class XDStatistics : RustPlugin {
#region ReferencePlugins
[PluginReference]
Plugin Friends, Clans, Battles, Duel, Economics, IQEconomic, ServerRewards, GameStoresRUST, RustStore, Duelist, EventHelper, ArenaTournament, ZLevelsRemastered, SkillTree, IQFakeActive;
private bool IsFriends(ulong userID, ulong targetID) {
if (Friends is not null) return Friends.Call("HasFriend", userID, targetID) is true;
return RelationshipManager.ServerInstance.playerToTeam.TryGetValue(userID, out RelationshipManager.PlayerTeam team) && team.members.Contains(targetID);
}
private bool IsClans(string userID, string targetID) {
if (Clans is null) return false;
string tagUserID = Clans.Call("GetClanOf", userID) as string;
string tagTargetID = Clans.Call("GetClanOf", targetID) as string;
return tagUserID is not null && tagUserID == tagTargetID;
}
private bool IsDuel(ulong userID) {
object playerId = ObjectCache.Get(userID);
BasePlayer player = (Duel is not null || Duelist is not null) ? BasePlayer.FindByID(userID) : null;
return EventAtEvent(playerId) || IsPlayerOnBattle(playerId) || IsPlayerOnActiveDuel(player) || InEvent(player) || IsOnTournament(playerId);
bool EventAtEvent(object id) => EventHelper?.Call("EMAtEvent", id) is true;
bool IsPlayerOnBattle(object id) => Battles?.Call<bool>("IsPlayerOnBattle", id) == true;
bool IsPlayerOnActiveDuel(BasePlayer pl) => Duel?.Call<bool>("IsPlayerOnActiveDuel", pl) == true;
bool InEvent(BasePlayer pl) => Duelist?.Call<bool>("inEvent", pl) == true;
bool IsOnTournament(object id) => ArenaTournament?.Call<bool>("IsOnTournament", id) == true;
}
#endregion
#region Var
private const bool RU = false;
public static XDStatistics Instance;
private ImageUI _imageUI;
private const string PermAdmin = "XDStatistics.admin";
private const string PermReset = "XDStatistics.reset";
private const string PermAvailability = "XDStatistics.availability";
private Dictionary<string, ItemDisplayName> _itemName = new();
private Dictionary<CatType, Dictionary<ulong, CashedData>> cashedTopUser = new();
public class CashedData {
public readonly int value;
public readonly float valueScore;
public readonly string playerName;
public int FullValue() {
if (valueScore == 0) return value;
return (int)valueScore;
}
public CashedData(int value, string playerName, float valueScore = 0) {
this.value = value;
this.playerName = playerName;
this.valueScore = valueScore;
}
}
private Dictionary<uint, string> _prefabID2Item = new();
private Dictionary<string, string> _prefabNameItem = new() {
["40mm_grenade_he"]
= "multiplegrenadelauncher", ["grenade.beancan.deployed"] = "grenade.beancan", ["grenade.f1.deployed"] = "grenade.f1", ["grenade.molotov.deployed"] = "grenade.molotov", ["grenade.flashbang.deployed"] = "grenade.flashbang", ["explosive.satchel.deployed"] = "explosive.satchel", ["explosive.timed.deployed"] = "explosive.timed", ["rocket_basic"] = "ammo.rocket.basic", ["rocket_hv"] = "ammo.rocket.hv", ["rocket_fire"] = "ammo.rocket.fire", ["survey_charge.deployed"] = "surveycharge"
}
;
private List<int> _alowedSeedId = new() {
1548091822, 1771755747, 1112162468, 1367190888, -1962971928, -2086926071, 44433072, -567909622, 1272194103, 854447607, 1660145984, 1783512007, -858312878
}
;
#endregion
#region Enum
private enum CatType {
Score, Killer, KillerNpc, KillerAnimal, PlayedTime, Gather, Explosive, Builder, Fermer
}
private enum StoreType {
GameStore, MoscowOVH, None
}
#endregion
#region Lang
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["PRINT_TOP_CATEGORY_Killer"]
= "Top 5 <color=#4286f4>Killers</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_KillerNpc"] = "Top 5 <color=#4286f4>NPC killers</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_KillerAnimal"] = "Top 5 <color=#4286f4>Animal killers</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Gather"] = "Top 5 <color=#4286f4>Farmers</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Explosive"] = "Top 5 <color=#4286f4>Explosions</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_PlayedTime"] = "Top 5 <color=#4286f4>Most Time Played</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Builder"] = "Top 5 <color=#4286f4>Builders</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Score"] = "Top 5 <color=#4286f4>Most points</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Fermer"] = "Top 5 <color=#4286f4>Fermer</color>\n{0}</size>", ["STAT_TOP_PLAYER_WIPE_Score"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>HIGHEST SCORE</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_PlayedTime"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>MOST TIME PLAYED</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_Explosive"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>MOST EXPLOSIONS</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_Gather"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>MOST CROPS FARMED</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_Killer"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>MOST KILLS</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_KillerNpc"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>NPC Killer</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_KillerAnimal"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>ANIMAL Killer</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_Builder"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>Builder</color>\nYou received a well deserved Reward!", ["STAT_TOP_PLAYER_WIPE_Fermer"] = "Congratulations!\nYou successfully held the {0} position in the previous wipe in the category <color=#4286f4>Fermer</color>\nYou received a well deserved Reward!", ["STAT_USER_TOTAL_GATHERED"] = "Total Gathered:", ["STAT_USER_TOTAL_EXPLODED"] = "Total Explosions:", ["STAT_USER_TOTAL_GROWED"] = "Total Farmed:", ["STAT_UI_MY_STAT"] = "My Statistics", ["STAT_UI_TOP_TEN"] = "Top 10 Players", ["STAT_UI_SEARCH"] = "Search", ["STAT_UI_INFO"] = "Player Information {0}", ["STAT_UI_ACTIVITY"] = "Activity", ["STAT_UI_ACTIVITY_TODAY"] = "Today: {0}", ["STAT_UI_ACTIVITY_TOTAL"] = "All Time: {0}", ["STAT_UI_SETTINGS"] = "Settings", ["STAT_UI_PLACE_TOP"] = "Place On Top: {0}", ["STAT_UI_SCORE"] = "Score: {0}", ["STAT_UI_PVP"] = "PvP Statistics", ["STAT_UI_FAVORITE_WEAPON"] = "Favorite Weapon", ["STAT_UI_PVP_KILLS"] = "Kills", ["STAT_UI_PVP_KILLS_NPC"] = "Kills NPC", ["STAT_UI_PVP_DEATH"] = "Deaths", ["STAT_UI_PVP_KDR"] = "K/D", ["STAT_UI_FAVORITE_WEAPON_KILLS"] = "Kills: {0}\nHits: {1}", ["STAT_UI_FAVORITE_WEAPON_NOT_DATA"] = "Data is still being calculated..", ["STAT_UI_OTHER_STAT"] = "Other Statistics", ["STAT_UI_HIDE_STAT"] = "Public Profile", ["STAT_UI_CONFIRM"] = "Are You Sure?", ["STAT_UI_CONFIRM_YES"] = "Yes", ["STAT_UI_CONFIRM_NO"] = "No", ["STAT_UI_RESET_STAT"] = "Reset Statistics", ["STAT_UI_CRATE_OPEN"] = "Crates Opened: {0}", ["STAT_UI_BARREL_KILL"] = "Barrels Destroyed: {0}", ["STAT_UI_ANIMAL_KILL"] = "Animal Kills: {0}", ["STAT_UI_HELI_KILL"] = "Helicopter Kills: {0}", ["STAT_UI_BRADLEY_KILL"] = "Bradley Kills: {0}", ["STAT_UI_NPC_KILL"] = "NPC Kills: {0}", ["STAT_UI_BTN_MORE"] = "Show More", ["STAT_UI_CATEGORY_GATHER"] = "Gather", ["STAT_UI_CATEGORY_EXPLOSED"] = "Explosions", ["STAT_UI_CATEGORY_PLANT"] = "Farming", ["STAT_UI_CATEGORY_TOP_KILLER"] = "Top 10 Killers", ["STAT_UI_CATEGORY_TOP_KILLER_ANIMALS"] = "Top 10 Animal Killers", ["STAT_UI_CATEGORY_TOP_NPCKILLER"] = "Top 10 NPC Killers", ["STAT_UI_CATEGORY_TOP_TIME"] = "Top 10 Most Time Played", ["STAT_UI_CATEGORY_TOP_GATHER"] = "Top 10 Gatherers", ["STAT_UI_CATEGORY_TOP_SCORE"] = "Top 10 Most Score", ["STAT_UI_CATEGORY_TOP_EXPLOSED"] = "Top 10 Explosions", ["STAT_UI_TOP_TEN_FOOTER"] = "The leaderboard updates every 3 minutes", ["STAT_PRINT_WIPE"] = "Wipe Detected. Data was successfully cleared!", ["STAT_PRINT_LOAD"] = "[SplitDatafile] Loaded {0} players | Cleared old players {1}", ["STAT_CMD_1"] = "No Permission!!", ["STAT_CMD_3"] = "The specified playername could not be found. Please use their SteamID.", ["STAT_CMD_4"] = "Found several players with similar names: {0}", ["STAT_CMD_5"] = "Player not found!", ["STAT_CMD_6"] = "Player {0} is already ignored", ["STAT_CMD_7"] = "You have successfully added a player {0} to the ignore list", ["STAT_CMD_8"] = "The player {0} is not in the ignore list", ["STAT_CMD_9"] = "You have successfully removed the player {0} from the ignore list", ["STAT_CMD_10"] = "Player {0} successfully credited {1} score", ["STAT_CMD_11"] = "Player {0} successfully removed {1} score", ["STAT_CMD_12"] = "List of available categories:\n{0}", ["STAT_COMMAND_SYNTAX_ERROR"] = "Incorrect syntax! Use: {0}", ["STAT_INVALID_PLAYER_ID_INPUT"] = "Invalid input! Please enter a valid player ID.", ["STAT_NOT_A_STEAM_ID"] = "The entered ID is not a SteamID. Please check and try again.", ["STAT_PLAYER_NOT_FOUND_BY_STEAMID"] = "Player with the specified Steam ID not found.", ["STAT_ADMIN_HIDE_STAT"] = "You've been added to the ignore list. You will not have access to the statistics. If this is an error, Please contact the Administrator!", ["STAT_TOP_VK_SCORE"] = "Топ {0} игрока по очкам\n {1}", ["STAT_TOP_VK_KILLER"] = "Топ {0} игрока по убийствам\n {1}", ["STAT_TOP_VK_TIME"] = "Топ {0} игрока по онлайну\n {1}", ["STAT_TOP_VK_FARM"] = "Топ {0} игрока по фарму\n {1}", ["STAT_TOP_VK_RAID"] = "Топ {0} игрока по рейдам\n {1}", ["STAT_TOP_VK_KILLER_NPC"] = "Топ {0} игрока по убийствам NPC\n {1}", ["STAT_TOP_VK_KILLER_ANIMAL"] = "Топ {0} игрока по убийствам животных\n {1}",
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
["PRINT_TOP_CATEGORY_Killer"]
= "Топ 5 <color=#4286f4>киллеры</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_KillerNpc"] = "Топ 5 <color=#4286f4>убийцы NPC</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_KillerAnimal"] = "Топ 5 <color=#4286f4>убийцы животных</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Gather"] = "Топ 5 <color=#4286f4>фармеры</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Explosive"] = "Топ 5 <color=#4286f4>рейдеры</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_PlayedTime"] = "Топ 5 <color=#4286f4>долгожителей</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Builder"] = "Топ 5 <color=#4286f4>строители</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Score"] = "Топ 5 по <color=#4286f4>очкам</color>\n{0}</size>", ["PRINT_TOP_CATEGORY_Fermer"] = "Топ 5 <color=#4286f4>фермеров</color>\n{0}</size>", ["STAT_TOP_PLAYER_WIPE_Score"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>SCORE</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_PlayedTime"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Долгожитель</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_Explosive"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Рейдер</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_Gather"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Добытчик</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_Killer"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Киллер</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_KillerNpc"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Убийца нпс</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_KillerAnimal"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Убийца животных</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_Builder"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Строитель</color>\nВы заслужено получаете награду!", ["STAT_TOP_PLAYER_WIPE_Fermer"] = "Поздравляю!\nВ прошлом вайпе вы успешно удерживали {0} позицию в категории <color=#4286f4>Фермер</color>\nВы заслужено получаете награду!", ["STAT_USER_TOTAL_GATHERED"] = "Всего добыто:", ["STAT_USER_TOTAL_EXPLODED"] = "Всего взорвано:", ["STAT_USER_TOTAL_GROWED"] = "Всего выращено:", ["STAT_UI_MY_STAT"] = "Моя Статистика", ["STAT_UI_TOP_TEN"] = "Топ 10 игроков", ["STAT_UI_SEARCH"] = "Поиск", ["STAT_UI_INFO"] = "Информация о профиле {0}", ["STAT_UI_ACTIVITY"] = "Активность", ["STAT_UI_ACTIVITY_TODAY"] = "Сегодня: {0}", ["STAT_UI_ACTIVITY_TOTAL"] = "За все время: {0}", ["STAT_UI_SETTINGS"] = "Настройки", ["STAT_UI_PLACE_TOP"] = "Место в топе: {0}", ["STAT_UI_SCORE"] = "SCORE: {0}", ["STAT_UI_PVP"] = "PVP статистика", ["STAT_UI_FAVORITE_WEAPON"] = "Фаворитное оружие", ["STAT_UI_PVP_KILLS"] = "Убийств", ["STAT_UI_PVP_KILLS_NPC"] = "Убийств NPC", ["STAT_UI_PVP_DEATH"] = "Смертей", ["STAT_UI_PVP_KDR"] = "K/D", ["STAT_UI_FAVORITE_WEAPON_KILLS"] = "Убийств: {0}\nПопаданий: {1}", ["STAT_UI_FAVORITE_WEAPON_NOT_DATA"] = "Данные еще собираются...", ["STAT_UI_OTHER_STAT"] = "Другая статистика", ["STAT_UI_HIDE_STAT"] = "Общедоступный профиль", ["STAT_UI_CONFIRM"] = "Вы уверены ?", ["STAT_UI_CONFIRM_YES"] = "Да", ["STAT_UI_CONFIRM_NO"] = "Нет", ["STAT_UI_RESET_STAT"] = "Обнулить статистику", ["STAT_UI_CRATE_OPEN"] = "Открыто ящиков: {0}", ["STAT_UI_BARREL_KILL"] = "Разбито бочек: {0}", ["STAT_UI_ANIMAL_KILL"] = "Убито животных: {0}", ["STAT_UI_HELI_KILL"] = "Сбито вертолетов: {0}", ["STAT_UI_BRADLEY_KILL"] = "Танков уничтожено: {0}", ["STAT_UI_NPC_KILL"] = "Убито NPC: {0}", ["STAT_UI_BTN_MORE"] = "Показать еще", ["STAT_UI_CATEGORY_GATHER"] = "Добыча", ["STAT_UI_CATEGORY_EXPLOSED"] = "Взрывчатка", ["STAT_UI_CATEGORY_PLANT"] = "Фермерство", ["STAT_UI_CATEGORY_TOP_KILLER"] = "Топ 10 киллеров", ["STAT_UI_CATEGORY_TOP_KILLER_ANIMALS"] = "Топ 10 убийц животных", ["STAT_UI_CATEGORY_TOP_NPCKILLER"] = "Топ 10 убийц npc", ["STAT_UI_CATEGORY_TOP_TIME"] = "Топ 10 по онлайну", ["STAT_UI_CATEGORY_TOP_GATHER"] = "Топ 10 фармил", ["STAT_UI_CATEGORY_TOP_SCORE"] = "Топ 10 по очкам", ["STAT_UI_CATEGORY_TOP_EXPLOSED"] = "Топ 10 рейдеров", ["STAT_UI_TOP_TEN_FOOTER"] = "Таблица лидеров обновляется каждые 3 минуты", ["STAT_PRINT_WIPE"] = "Произошел вайп. Данные успешно удалены!", ["STAT_PRINT_LOAD"] = "[SplitDatafile] Загружено {0} игроков | Очистили старых игроков {1}", ["STAT_CMD_1"] = "Недостаточно прав!", ["STAT_CMD_3"] = "Указанный игрок не найден. Для более точного поиска укажите его SteamID.", ["STAT_CMD_4"] = "Найдено несколько игроков с похожим именем: {0}", ["STAT_CMD_5"] = "Игрок не найден!", ["STAT_CMD_6"] = "Игрок {0} уже игнорируется", ["STAT_CMD_7"] = "Вы успешно добавили игрока {0} в игнор лист", ["STAT_CMD_8"] = "Игрока {0} нет в списке игнорируемых", ["STAT_CMD_9"] = "Вы успешно убрали игрока {0} из игнор листа", ["STAT_CMD_10"] = "Игроку {0} успешно зачислено {1} очков", ["STAT_CMD_11"] = "Игроку {0} успешно снято {1} очков", ["STAT_CMD_12"] = "Список доступных категорий:\n{0}", ["STAT_COMMAND_SYNTAX_ERROR"] = "Неверный синтаксис! Используйте: {0}", ["STAT_INVALID_PLAYER_ID_INPUT"] = "Неверный ввод! Пожалуйста, введите корректный ID игрока.", ["STAT_NOT_A_STEAM_ID"] = "Введенный ID не является SteamID. Пожалуйста, проверьте и попробуйте снова.", ["STAT_PLAYER_NOT_FOUND_BY_STEAMID"] = "Игрок с указанным Steam ID не найден.", ["STAT_ADMIN_HIDE_STAT"] = "Вы добавлены в игнор лист. У вас нет доступа к статистики, если это ошибка, свяжитесь с администратором!", ["STAT_TOP_VK_SCORE"] = "Топ {0} игрока по очкам\n {1}", ["STAT_TOP_VK_KILLER"] = "Топ {0} игрока по убийствам\n {1}", ["STAT_TOP_VK_TIME"] = "Топ {0} игрока по онлайну\n {1}", ["STAT_TOP_VK_FARM"] = "Топ {0} игрока по фарму\n {1}", ["STAT_TOP_VK_RAID"] = "Топ {0} игрока по рейдам\n {1}", ["STAT_TOP_VK_KILLER_NPC"] = "Топ {0} игрока по убийствам NPC\n {1}", ["STAT_TOP_VK_KILLER_ANIMAL"] = "Топ {0} игрока по убийствам животных\n {1}",
}
, this, "ru");
}
#endregion
#region Configuration
private Configuration _config;
private class Configuration {
public class SettingsInterface {
[JsonProperty(RU ? "Цвет плашки заднего фона в топ 10 за 1 место" : "Background color in the top 10 for 1st place")]
public string ColorTop1 = "1 0.8431373 0 0.49";
[JsonProperty(RU ? "Цвет плашки заднего фона в топ 10 за 2 место" : "Background color in the top 10 for 2nd place")]
public string ColorTop2 = "0.7529412 0.7529412 0.7529412 0.49";
[JsonProperty(RU ? "Цвет плашки заднего фона в топ 10 за 3 место" : "Background color in the top 10 for 3rd place")]
public string ColorTop3 = "0.80392 0.49803 0.1960784 0.49";
[JsonProperty(RU ? "Использовать альтернативный задний фон ?" : "Should I use an alternative background?")]
public bool useCustomBackGround = false;
[JsonProperty(RU ? "Цвет заднего фона" : "Background color")]
public string BackgroundColor = "0.235 0.227 0.180 0.90";
[JsonProperty(RU ? "Цвет спрайта и материала" : "The color of the sprite and the material")]
public string BackgroundColorSpriteAndMaterial = "0.141 0.137 0.109 1";
[JsonProperty("Sprite")]
public string BackgroundSprite = "assets/content/ui/ui.background.transparent.radial.psd";
[JsonProperty("Material")]
public string BackgroundMaterial = "assets/content/ui/namefontmaterial.mat";
}
public class DiscordMessage {
[JsonProperty(RU ? "Отправлять в Discord топ-5 лучших игроков по разным категориям?" : "Send top 5 best players in various categories to Discord?")]
public bool discordTopFiveUse = false;
[JsonProperty(RU ? "Список категорий для отправки случайных топ 5 игроков в категории в Discord (Список доступных категорий - stat.category list)" : "List of categories for sending random top 5 players in a category on Discord (List of available categories - stat.category list)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> discordCategories = new() {
"Score", "Killer", "KillerNpc", "KillerAnimal", "PlayedTime", "Gather", "Explosive", "Builder", "Fermer"
}
;
[JsonIgnore]
public List<CatType> _discordCategories = new();
[JsonProperty(RU ? "Как часто отправлять сообщение? (Секунды)" : "How often should the message be sent? (Seconds)")]
public int discordSendTopTime = 600;
[JsonProperty(RU ? "WebHook Discord" : "Discord WebHook")]
public string weebHook = string.Empty;
[JsonProperty(RU ? "Цвет линии в сообщении (или несколько цветов)" : "The color(s) of the line in the message", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public int[] colorLines = new int[] {
53380, 9359868, 11253955
}
;
[JsonProperty(RU ? "Дополнительный текст к сообщению" : "Additional text for the message")]
public string message = string.Empty;
}
public class ChatMessage {
[JsonProperty(RU ? "Отправлять в чат сообщения с топ-5 игроками по разным категориям" : "Send chat messages with top 5 players in various categories")]
public bool chatSendTop = true;
[JsonProperty(RU ? "Список категорий для отправки случайных топ 5 игроков в категории в игровой чат (Список доступных категорий - stat.category list)" : "List of categories for sending random top 5 players in a category to the game chat (List of available categories - stat.category list)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> chatCategories = new() {
"Score", "Killer", "KillerNpc", "KillerAnimal", "PlayedTime", "Gather", "Explosive", "Builder", "Fermer"
}
;
[JsonIgnore]
public List<CatType> _chatCategories = new();
[JsonProperty(RU ? "Как часто отправлять сообщение? (Секунды)" : "How often should the message be sent? (Seconds)")]
public int chatSendTopTime = 600;
}
public class Settings {
[JsonProperty(RU ? "Чат-команды для открытия статистики" : "Chat commands for opening statistics", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> chatCommandOpenStat = new List<string> {
"stat", "top"
}
;
[JsonProperty(RU ? "Консольная команда для открытия статистики" : "Console command to open statistics")]
public string consoleCommandOpenStat = "stat";
[JsonProperty(RU ? "Включить возможность сбросить свою статистику? (требуется XDStatistics.reset)" : "Enable the ability to reset your stats? (requires XDStatistics.reset)")]
public bool dropStatUse = false;
[JsonProperty(RU ? "Включить возможность скрыть свою статистику от других пользователей? (требуется XDStatistics.availability)" : "Enable the ability to hide your statistics from other users? (requires XDStatistics.availability)")]
public bool availabilityUse = true;
[JsonProperty(RU ? "Очищать данные при вайпе" : "Clear data when wiped")]
public bool wipeData = true;
[JsonProperty(RU ? "Как часто (в минутах) будут сохраняться данные?" : "How often (in minutes) will data be saved?")]
public int dataSaveTime = 30;
[JsonProperty(RU ? "Учитывать убийства NPC для выбора любимого оружия?" : "Consider NPC kills to choose your favorite weapon?")]
public bool npsDeathUse = false;
[JsonProperty(RU ? "Учитывать убийства NPC в основную статистику убийств" : "Include NPC kills in the main kill statistics")]
public bool npsDeathUseKils = false;
[JsonProperty(RU ? "У вас сервер в режиме PVE?" : "Is your server in PVE mode?")]
public bool pveServerMode = false;
[JsonProperty(RU ? "Список игроков (SteamID), которые не будут включены в статистику" : "List of players (SteamID) who will not be included in the statistics", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ulong> ignoreList = new List<ulong>();
[JsonProperty(RU ? "IQFakeActive : Использовать совместную работу (true - да/false - нет)" : "IQFakeActive : Use collaboration (true - yes/false - no)")]
public Boolean IQFakeActiveUse;
}
public class SettingsIgnore {
[JsonProperty(RU ? "Засчитывать убийства спящих(оффлайн) игроков?" : "Count killings of sleeping(offline) players?")]
public bool countSleepingPlayerKills = true;
[JsonProperty(RU ? "Засчитывать убийства игроков, у которых нет в инвентаре предметов из категории оружия?" : "Count killings of players without any weapons in inventory?")]
public bool countKillsWithoutWeapons = true;
[JsonProperty(RU ? "Черный список предметов для игнорирования (Эти предметы не будут считаться оружием)" : "Blacklist of items to ignore (These items won't be considered as weapons)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> itemsToIgnoreList = new() {
"Rock",
}
;
}
public class SettingsScore {
[JsonProperty(RU ? "Очки за крафт" : "Points for crafting")]
public float craftScore = 1;
[JsonProperty(RU ? "Очки за разбивание бочек" : "Points for breaking barrels")]
public float barrelScore = 1;
[JsonProperty(RU ? "Очки за установку строительных блоков" : "Points for placing building blocks")]
public float BuildingScore = 1;
[JsonProperty(RU ? "Очки за использование взрывчатых предметов" : "Points for using explosive items")]
public Dictionary<string, float> ExplosionScore = new();
[JsonProperty(RU ? "Очки за добычу ресурсов" : "Points for gathering resources")]
public Dictionary<string, float> GatherScore = new();
[JsonProperty(RU ? "Очки за найденный скрап" : "Points for collected scrap")]
public float ScrapScore = 0.5f;
[JsonProperty(RU ? "Очки за сбор урожая (с плантации)" : "Points for harvesting (from plantation)")]
public float PlantScore = 0.2f;
[JsonProperty(RU ? "Очки за убийство животных" : "Points for killing animals")]
public float AnimalScore = 1;
[JsonProperty(RU ? "Очки за сбитие вертолета" : "Points for shooting down a helicopter")]
public float HeliScore = 5;
[JsonProperty(RU ? "Очки за взрыв танка" : "Points for blowing up a tank")]
public float BradleyScore = 5;
[JsonProperty(RU ? "Очки за убийство NPC" : "Points for killing NPCs")]
public float NpcScore = 5;
[JsonProperty(RU ? "Очки за убийство игроков" : "Points for killing players")]
public float PlayerScore = 10;
[JsonProperty(RU ? "Очки за проведенное время на сервере (за каждую минуту)" : "Points for time spent on the server (per minute)")]
public float TimeScore = 0.2f;
[JsonProperty(RU ? "Сколько очков отнять за самоубийство?" : "How many points to deduct for suicide?")]
public float SuicideScore = 2;
[JsonProperty(RU ? "Сколько очков отнять за смерть?" : "How many points to deduct for death?")]
public float DeathScore = 1;
[JsonProperty(RU ? "Черный список ресурсов и взрывчатых предметов" : "Blacklist of resources and explosive items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> blackListed = new() {
"ammo.rifle.explosive"
}
;
[JsonProperty(RU ? "Черный список предметов для крафта" : "Blacklist of crafting items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> blackListedCraft = new() {
"photo", "note"
}
;
}
public class SettingsPrize {
[JsonProperty(RU ? "Использовать автоматическую выдачу наград при вайпе сервера" : "Enable automatic prize distribution on server wipe")]
public bool prizeUse = false;
[JsonProperty(RU ? "[GameStores] ID магазина" : "[RUSSIA][GameStores] Shop ID")]
public string ShopID = "";
[JsonProperty(RU ? "[GameStores] ID сервера" : "[RUSSIA][GameStores] Server ID")]
public string ServerID = "";
[JsonProperty(RU ? "[GameStores] Секретный ключ" : "[RUSSIA][GameStores] Secret Key")]
public string SecretKey = "";
[JsonProperty(RU ? "Настройка наград по категориям (Key - Название категории) (Список доступных категорий - stat.category list)" : "Configure prizes by category (Key - Category name) (Available categories - stat.category list)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, List<Reward>> PrizeByCategory = new() {
["Score"]
= new List<Reward>() {
new() {
top = 1
}
, new() {
top = 2
}
, new() {
top = 3
}
}
}
;
[JsonIgnore]
public List<CatType> rewardsCategoryList = new();
public class Reward {
[JsonProperty(RU ? "За какое место вручать данную награду награду" : "Which ranking to award? (from 1 to 3). If you don't need to award this category, remove all related rewards.")]
public int top = 1;
[JsonProperty(RU ? "Дополнительный текст, получаемый игроком при входе на сервер (например, информация о полученной награде)" : "Additional text received by the player upon server entry (e.g., information about the reward received)")]
public string entryRewardMessage = "";
[JsonProperty(RU ? "Выдавать награду после того как игрок зайдет на сервер ? (Нужно для типов наград где требуется чтоб игрок был онлайн. Например экономики)" : "Give the reward after the player logs into the server? (Needed for reward types that require the player to be online, such as economy rewards)")]
public bool rewardOnConnected = false;
public class CommandPrize {
[JsonProperty(RU ? "Выдавать награду в виде команды?" : "Award prize as a command?")]
public bool commandPrizeUse = false;
[JsonProperty(RU ? "Команды, используемые в качестве приза (%STEAMID% - Player ID)" : "Commands used as prizes (%STEAMID% - Player ID)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> commandPrizeList = new() {
"[Examples] oxide.grant user %STEAMID% Plugins.Permissions", "[Examples - IQPermissions] grantperm %STEAMID% Permission Time"
}
;
}
public class EconomicsPrize {
[JsonProperty(RU ? "Выдавать награду в виде экономики (IQEconomic/Economics/ServerRewards)" : "Award prize in economic currency (IQEconomic/Economics/ServerRewards)")]
public bool economicsPrizeUse = false;
[JsonProperty(RU ? "Количество начисляемой валюты в экономику (IQEconomic/Economics/ServerRewards)" : "Amount of currency to credit in economy (IQEconomic/Economics/ServerRewards)")]
public int balanceEconomicsPlus = 100;
}
public class MarketPlacePrize {
[JsonProperty(RU ? "Выдавать награду в виде баланса в магазине (GameStore/MoscowOVH)" : "[RUSSIA]Award prize as store balance (GameStore/MoscowOVH)?")]
public bool marketplacePrizeUse = false;
[JsonProperty(RU ? "Выбор магазина для награды (GameStore - 0, MoscowOVH - 1, None - 2)" : "[RUSSIA]Select store for prize distribution (GameStore - 0, MoscowOVH - 1, None - 2)")]
public StoreType StoreType = StoreType.None;
[JsonProperty(RU ? "Сумма начисления на баланс (GameStore/MoscowOVH)" : "[RUSSIA]Amount to credit to balance (GameStore/MoscowOVH)")]
public int balancePlus = 30;
[JsonProperty(RU ? "Сообщение для истории покупок в GameStore" : "[RUSSIA]GameStore purchase history message")]
public string balancePlusMess = string.Empty;
}
[JsonProperty(RU ? "Настройка команд как награды" : "Configure commands as rewards")]
public CommandPrize rewardCommandsConfiguration = new();
[JsonProperty(RU ? "Настройка награды в виде экономики (IQEconomic/Economics/ServerRewards)" : "Configure economic rewards (IQEconomic/Economics/ServerRewards)")]
public EconomicsPrize rewardEconomicsConfiguration = new();
[JsonProperty(RU ? "Настройка награды в виде баланса (GameStore/MoscowOVH)" : "[RUSSIA]Configure prize as balance (GameStore/MoscowOVH)")]
public MarketPlacePrize rewardMarketPlaceConfiguration = new();
public void RewardPlayer(string userID) {
if (rewardCommandsConfiguration is {
commandPrizeUse : true
}
) foreach (string command in rewardCommandsConfiguration.commandPrizeList) Instance.Server.Command(command.Replace("%STEAMID%", userID));
if (rewardEconomicsConfiguration is {
economicsPrizeUse : true
}
) {
if (Instance?.Economics) {
Instance.Economics.Call("Deposit", ulong.Parse(userID), (double)rewardEconomicsConfiguration.balanceEconomicsPlus);
}
else if (Instance?.IQEconomic) {
Instance.IQEconomic.Call("API_SET_BALANCE", ulong.Parse(userID), rewardEconomicsConfiguration.balanceEconomicsPlus);
}
else if (Instance?.ServerRewards) {
Instance.ServerRewards.Call("AddPoints", ulong.Parse(userID), rewardEconomicsConfiguration.balanceEconomicsPlus);
}
}
if (rewardMarketPlaceConfiguration is {
marketplacePrizeUse : true
}
) {
switch (rewardMarketPlaceConfiguration.StoreType) {
case StoreType.GameStore : string message = string.IsNullOrWhiteSpace(rewardMarketPlaceConfiguration.balancePlusMess) ? "API XDStatistics" : rewardMarketPlaceConfiguration.balancePlusMess;
string uri = $"https://gamestores.app/api?shop_id={Instance._config.settingsPrize.ShopID}&secret={Instance._config.settingsPrize.SecretKey}&server={Instance._config.settingsPrize.ServerID}&action=moneys&type=plus&steam_id={userID}&amount={rewardMarketPlaceConfiguration.balancePlus}&mess={message}";
Instance.webrequest.Enqueue(uri, "", (code, response) => {
switch (code) {
case 0 : {
Instance.PrintError("Api does not responded to a request");
break;
}
case 200 : {
break;
}
case 404 : {
Instance.PrintError($"Please check your configuration! {code}");
break;
}
}
}
, Instance);
break;
case StoreType.MoscowOVH : if (Instance?.RustStore) {
Instance.RustStore.CallHook("APIChangeUserBalance", ulong.Parse(userID), rewardMarketPlaceConfiguration.balancePlus, new Action<string>(result => {
if (result == "SUCCESS") return;
Interface.Oxide.LogDebug($"Баланс игрока {ulong.Parse(userID)} не был изменен, ошибка: {result}");
}
));
}
break;
case StoreType.None : break;
default : throw new ArgumentOutOfRangeException();
}
}
}
}
}
[JsonProperty(RU ? "Основные настройки плагина" : "Basic Plugin Settings")]
public Settings settings = new Settings();
[JsonProperty(RU ? "Настройка начисления очков" : "Points Allocation Settings")]
public SettingsScore settingsScore = new SettingsScore();
[JsonProperty(RU ? "Настройки наград для лидеров по выбраным категории в конце вайпа" : "Reward settings for leaders in selected categories at the end of the wipe")]
public SettingsPrize settingsPrize = new SettingsPrize();
[JsonProperty(RU ? "Настройки интерфейса" : "Interface Settings")]
public SettingsInterface settingsInterface = new SettingsInterface();
[JsonProperty(RU ? "Настройка отправки топ игроков в Discord" : "Setting up the sending of top players to Discord")]
public DiscordMessage discordMessage = new DiscordMessage();
[JsonProperty(RU ? "Настройка отправки топ игроков в игровой чат" : "Setting up the sending of top players to the game chat")]
public ChatMessage chatMessage = new ChatMessage();
[JsonProperty(RU ? "Настройка параметров статистики убийств" : "Configure kill statistics settings")]
public SettingsIgnore settingsIgnore = new SettingsIgnore();
}
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<Configuration>();
if (_config == null) LoadDefaultConfig();
ValidateConfig();
SaveConfig();
}
catch (JsonException ex) {
Debug.LogException(ex);
LoadDefaultConfig();
}
}
private void ValidateConfig() {
if (_config.settingsScore.GatherScore.Count == 0) {
_config.settingsScore.GatherScore = new Dictionary<string, float> {
["wood"]
= 0.3f, ["stones"] = 0.6f, ["metal.ore"] = 1, ["sulfur.ore"] = 1.5f, ["hq.metal.ore"] = 2,
}
;
}
if (_config.settingsScore.ExplosionScore.Count == 0) {
_config.settingsScore.ExplosionScore = new Dictionary<string, float> {
["explosive.timed"]
= 2, ["explosive.satchel"] = 0.7f, ["grenade.beancan"] = 0.3f, ["grenade.f1"] = 0.1f, ["ammo.rocket.basic"] = 1, ["ammo.rocket.hv"] = 0.5f, ["ammo.rocket.fire"] = 0.7f, ["ammo.rifle.explosive"] = 0.02f,
}
;
}
if (_config.settingsPrize.prizeUse && _config.settingsPrize.PrizeByCategory.Count > 0) {
foreach (KeyValuePair<string, List<Configuration.SettingsPrize.Reward>> pbc in _config.settingsPrize.PrizeByCategory) if (Enum.TryParse(pbc.Key, out CatType categoryType)) _config.settingsPrize.rewardsCategoryList.Add(categoryType);
else PrintError($"[Configuration Reward ERROR] Invalid category type: {pbc.Key}. Removing from list.\nAvailable types: {string.Join(", ", Enum.GetNames(typeof(CatType)))}");
}
ProcessCategories(_config.chatMessage.chatSendTop, _config.chatMessage.chatCategories, _config.chatMessage._chatCategories);
ProcessCategories(_config.discordMessage.discordTopFiveUse, _config.discordMessage.discordCategories, _config.discordMessage._discordCategories);
return;
void ProcessCategories(bool condition, List<string> categories, ICollection<CatType> targetList) {
if (!condition || categories.Count == 0) return;
foreach (string category in categories) {
if (Enum.TryParse(category, out CatType categoryType)) {
targetList.Add(categoryType);
}
else {
PrintError($"[Configuration] Invalid category type: {category}. Removing from list.\nAvailable types: {string.Join(", ", Enum.GetNames(typeof(CatType)))}");
}
}
}
}
protected override void SaveConfig() {
Config.WriteObject(_config);
}
protected override void LoadDefaultConfig() {
_config = new Configuration();
}
#endregion
#region Data
private static List<ulong> ignoreReservedPlayer = new();
private static Dictionary<ulong, List<PrizePlayer>> _prizePlayerData = new();
private class PrizePlayer {
public CatType catType;
public int value;
public int top;
public bool isRewarded;
}
private void LoadDataIgnoreList() => ignoreReservedPlayer = Interface.Oxide.DataFileSystem.ReadObject<List<ulong>>("XDStatistics/IgnoredPlayers");
private void SaveDataIgnoreList() => Interface.Oxide.DataFileSystem.WriteObject("XDStatistics/IgnoredPlayers", ignoreReservedPlayer);
private void LoadDataPrize() => _prizePlayerData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, List<PrizePlayer>>>("XDStatistics/PlayersRewarded");
private void SaveDataPrize() => Interface.Oxide.DataFileSystem.WriteObject("XDStatistics/PlayersRewarded", _prizePlayerData);
#endregion
#region SplitDatafile
private abstract class SplitDatafile<T> where T : SplitDatafile<T>, new() {
public static Dictionary<ulong, T> _players = new();
protected static string[] GetFiles(string baseFolder) {
try {
int json = ".json".Length;
string[] paths = Interface.Oxide.DataFileSystem.GetFiles(baseFolder, "*.json");
for (int i = 0; i < paths.Length; i++) {
string path = paths[i];
int separatorIndex = path.LastIndexOf("/", StringComparison.Ordinal);
paths[i] = path.Substring(separatorIndex + 1, path.Length - separatorIndex - 1 - json);
}
return paths;
}
catch {
return Array.Empty<string>();
}
}
protected static void Delete(string baseFolder, ulong userId) {
if (!_players.Remove(userId, out T _)) return;
try {
Interface.Oxide.DataFileSystem.DeleteDataFile(baseFolder + userId);
}
catch (Exception e) {
Interface.Oxide.LogError(e.ToString());
}
}
protected static T Save(string baseFolder, ulong userId) {
T data;
if (!_players.TryGetValue(userId, out data)) return null;
Interface.Oxide.DataFileSystem.WriteObject(baseFolder + userId, data);
return data;
}
protected static T Get(string baseFolder, ulong userId) {
T data;
if (_players.TryGetValue(userId, out data)) return data;
return null;
}
protected static T Load(string baseFolder, ulong userId) {
T data = null;
try {
data = Interface.Oxide.DataFileSystem.ReadObject<T>(baseFolder + userId);
}
catch (Exception e) {
Interface.Oxide.LogError(e.ToString());
}
return _players[userId] = data;
}
protected static T GetOrLoad(string baseFolder, ulong userId) {
T data;
if (_players.TryGetValue(userId, out data)) return data;
return Load(baseFolder, userId);
}
protected static T GetOrCreate(string baseFolder, ulong userId) {
return GetOrLoad(baseFolder, userId) ?? (_players[userId] = new T());
}
protected static T ClearAndSave(string baseFolder, ulong userId) {
if (_players.TryGetValue(userId, out T data)) {
data = new T();
_players[userId] = data;
Interface.Oxide.DataFileSystem.WriteObject(baseFolder + userId, data);
return data;
}
return null;
}
}
private class PlayerInfo : SplitDatafile<PlayerInfo> {
private const string BaseFolder = "XDStatistics" + "/" + "Players" + "/";
public static PlayerInfo Save(ulong userId) => Save(BaseFolder, userId);
public static void Delete (ulong userId) => Delete(BaseFolder, userId);
public static PlayerInfo Get(ulong userId) => Get(BaseFolder, userId);
public static PlayerInfo Load(ulong userId) => Load(BaseFolder, userId);
public static PlayerInfo Clear(ulong userId) => ClearAndSave(BaseFolder, userId);
public static PlayerInfo GetOrLoad(ulong userId) => GetOrLoad(BaseFolder, userId);
public static PlayerInfo GetOrCreate(ulong userId) => GetOrCreate(BaseFolder, userId);
public static string[] GetFiles() => GetFiles(BaseFolder);
#region NewData
#region MiscData
[JsonIgnore]
public bool IsFake;
[JsonIgnore]
public bool IsIgnore;
[JsonIgnore]
public bool IsChanged = false;
public string Name = s
// ... [Code preview truncated due to length. Full source in attached file] ...