C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using UnityEngine;
using Facepunch.Steamworks;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using Oxide.Game.Rust.Cui;
namespace Oxide.Plugins {
[Info("SkinBox", "FuJiCuRa", "1.12.8", ResourceId = 17)]
// Слив плагинов server-rust by Apolo YouGame
class SkinBox : RustPlugin {
[PluginReference]
Plugin QuickSort, SortItems, ServerRewards, Economics, StacksExtended;
bool skinsLoaded;
bool Initialized;
bool Changed;
static SkinBox skinBox = null;
bool isValidated;
bool activeServerRewards;
bool activeEconomics;
bool activePointSystem;
int maxItemsShown;
bool _hasStacksExtended;
bool _stacksExtendedExtrasDisabled;
Dictionary<string, LinkedList<ulong>> skinsCache = new Dictionary<string, LinkedList<ulong>>();
Dictionary<string, LinkedList<ulong>> skinsCacheLimited = new Dictionary<string, LinkedList<ulong>>();
Dictionary<string, int> approvedSkinsCount = new Dictionary<string, int>();
List<ulong> checkedPlayers = new List<ulong>();
Dictionary<string, DateTime> cooldownTimes = new Dictionary<string, DateTime>();
Dictionary<string, string> NameToItemName = new Dictionary<string, string>();
Dictionary<string, string> ItemNameToName = new Dictionary<string, string>();
Dictionary<string, object> manualAddedSkinsPre = new Dictionary<string, object>();
Dictionary<string, List<ulong>> manualAddedSkins = new Dictionary<string, List<ulong>>();
List<ulong> excludedSkins = new List<ulong>();
List<object> excludedSkinsPre = new List<object>();
Dictionary<ulong, Vector3> activeBoxUsers = new Dictionary<ulong, Vector3>();
Dictionary<ulong, SkinBoxHandler> activeSkinBoxes = new Dictionary<ulong, SkinBoxHandler>();
Dictionary<ulong, string> skinWorkshopNames = new Dictionary<ulong, string>();
List<Workshop.Query> getRankedQueries;
List<Workshop.Query> getApprovedQueries;
int countQueriedGetRanked;
int countQueriedGetApproved;
IEnumerator _getApprovedWorkshopSkins;
IEnumerator _getRankedWorkshopSkins;
IEnumerator _validateManualAddedSkins;
IEnumerator _searchWorkshopSkin;
string skinBoxCommand;
string permissionUse;
bool forceClientPreload;
bool forceClientAlways;
bool forceAlsoWearables;
bool showLoadedSkinCounts;
int exludedSkinsAuthLevel;
bool hideQuickSort;
bool hideSortItems;
float boxUseDistance;
string steamApiKey;
int accessOverrideAuthLevel;
bool allowStackedItems;
bool enableCustomPerms;
string permCustomPlayerwearable;
string permCustomWeapon;
string permCustomDeployable;
bool useInbuiltSkins;
bool useApprovedSkins;
int approvedSkinsLimit;
bool useManualAddedSkins;
bool useWebskinsRankedByTrend;
int usedRankedByTrendDays;
int usedThreadsGetRanked;
int maxPagesShown;
bool enableCooldown;
int cooldownBox;
bool cooldownOverrideAdmin;
int cooldownOverrideAuthLevel;
bool activateAfterSkinTaken;
bool enableUsageCost;
bool useServerRewards;
bool useEconomics;
int costBoxOpen;
int costWeapon;
int costPlayerwearable;
int costDeployable;
bool costExcludeAdmins;
string costExcludePerm;
bool costExcludePermEnabled;
object GetConfig(string menu, string datavalue, object defaultValue) {
var data = Config[menu] as Dictionary<string, object>;
if (data == null) {
data = new Dictionary<string, object>();
Config[menu] = data;
Changed = true;
}
object value;
if (!data.TryGetValue(datavalue, out value)) {
value = defaultValue;
data[datavalue] = value;
Changed = true;
}
return value;
}
void LoadVariables() {
useInbuiltSkins = Convert.ToBoolean(GetConfig("AvailableSkins", "useInbuiltSkins", true));
useApprovedSkins = Convert.ToBoolean(GetConfig("AvailableSkins", "useApprovedSkins", true));
approvedSkinsLimit = Convert.ToInt32(GetConfig("AvailableSkins", "approvedSkinsLimit", -1));
useManualAddedSkins = Convert.ToBoolean(GetConfig("AvailableSkins", "useManualAddedSkins", true));
useWebskinsRankedByTrend = Convert.ToBoolean(GetConfig("AvailableSkins", "useWebskinsRankedByTrend", false));
usedRankedByTrendDays = Convert.ToInt32(GetConfig("AvailableSkins", "usedRankedByTrendDays", 30));
usedThreadsGetRanked = Convert.ToInt32(GetConfig("AvailableSkins", "usedThreadsGetRanked", 3));
maxPagesShown = Convert.ToInt32(GetConfig("AvailableSkins", "maxPagesShown", 1));
skinBoxCommand = Convert.ToString(GetConfig("Settings", "skinBoxCommand", "skinbox"));
permissionUse = Convert.ToString(GetConfig("Settings", "permissionUse", "skinbox.use"));
forceClientPreload = Convert.ToBoolean(GetConfig("Settings", "forceClientPreload", false));
forceClientAlways = Convert.ToBoolean(GetConfig("Settings", "forceClientAlways", false));
forceAlsoWearables = Convert.ToBoolean(GetConfig("Settings", "forceAlsoWearables", true));
showLoadedSkinCounts = Convert.ToBoolean(GetConfig("Settings", "showLoadedSkinCounts", true));
exludedSkinsAuthLevel = Convert.ToInt32(GetConfig("Settings", "exludedSkinsAuthLevel", 2));
accessOverrideAuthLevel = Convert.ToInt32(GetConfig("Settings", "accessOverrideAuthLevel", 2));
hideQuickSort = Convert.ToBoolean(GetConfig("Settings", "hideQuickSort", true));
hideSortItems = Convert.ToBoolean(GetConfig("Settings", "hideSortItems", true));
allowStackedItems = Convert.ToBoolean(GetConfig("Settings", "allowStackedItems", false));
boxUseDistance = Convert.ToSingle(GetConfig("Settings", "boxUseDistance", 5.0));
steamApiKey = Convert.ToString(GetConfig("Settings", "steamApiKey", ""));
enableCustomPerms = Convert.ToBoolean(GetConfig("CustomPermissions", "enableCustomPerms", false));
permCustomPlayerwearable = Convert.ToString(GetConfig("CustomPermissions", "permCustomPlayerwearable", "skinbox.playerwearable"));
permCustomWeapon = Convert.ToString(GetConfig("CustomPermissions", "permCustomWeapon", "skinbox.weapon"));
permCustomDeployable = Convert.ToString(GetConfig("CustomPermissions", "permCustomDeployable", "skinbox.deployable"));
enableCooldown = Convert.ToBoolean(GetConfig("Cooldown", "enableCooldown", false));
cooldownBox = Convert.ToInt32(GetConfig("Cooldown", "cooldownBox", 60));
cooldownOverrideAdmin = Convert.ToBoolean(GetConfig("Cooldown", "cooldownOverrideAdmin", true));
cooldownOverrideAuthLevel = Convert.ToInt32(GetConfig("Cooldown", "cooldownOverrideAuthLevel", 2));
activateAfterSkinTaken = Convert.ToBoolean(GetConfig("Cooldown", "activateAfterSkinTaken", true));
manualAddedSkinsPre = (Dictionary<string, object>)GetConfig("SkinsAdded", "SkinList", new Dictionary<string, object> {
}
);
excludedSkinsPre = (List<object>)GetConfig("SkinsExcluded", "SkinList", new List<object> {
}
);
enableUsageCost = Convert.ToBoolean(GetConfig("UsageCost", "enableUsageCost", false));
useServerRewards = Convert.ToBoolean(GetConfig("UsageCost", "useServerRewards", true));
useEconomics = Convert.ToBoolean(GetConfig("UsageCost", "useEconomics", false));
costBoxOpen = Convert.ToInt32(GetConfig("UsageCost", "costBoxOpen", 5));
costWeapon = Convert.ToInt32(GetConfig("UsageCost", "costWeapon", 30));
costPlayerwearable = Convert.ToInt32(GetConfig("UsageCost", "costPlayerwearable", 20));
costDeployable = Convert.ToInt32(GetConfig("UsageCost", "costDeployable", 10));
costExcludeAdmins = Convert.ToBoolean(GetConfig("UsageCost", "costExcludeAdmins", true));
costExcludePerm = Convert.ToString(GetConfig("UsageCost", "costExcludePerm", "skinbox.costexcluded"));
costExcludePermEnabled = Convert.ToBoolean(GetConfig("UsageCost", "costExcludePermEnabled", false));
var configremoval = false;
if ((Config.Get("AvailableSkins") as Dictionary<string, object>).ContainsKey("MissingSkinNames")) {
(Config.Get("AvailableSkins") as Dictionary<string, object>).Remove("MissingSkinNames");
configremoval = true;
}
if (!Changed & !configremoval) return;
SaveConfig();
Changed = false;
configremoval = false;
}
protected override void LoadDefaultConfig() {
Config.Clear();
LoadVariables();
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
{
"NoPermission", "У вас нет доступа к этой команде!"
}
, //{"ToNearPlayer", "The SkinBox is currently not usable at this place"},
{
"CooldownTime", "Нельзя так часто использовать команду: /skin.\nПодождите: {0} секунд."
}
, {
"NotEnoughBalanceOpen", "У вас не хватает «{0} $» что бы воспользоваться командой: /skin"
}
, {
"NotEnoughBalanceUse", "Вам необходимо «{0} $» что бы поставить '{1}'"
}
, {
"NotEnoughBalanceTake", "Скин '{0}' не был очищен!\nУ вас не хватило денег!"
}
,
}
, this);
}
void Loaded() {
LoadVariables();
LoadDefaultMessages();
UnsubscribeAll();
if (allowStackedItems) SubscribeSplit();
else UnsubscribeSplit();
cmd.AddChatCommand(skinBoxCommand, this, "cmdSkinBox");
if (!permission.PermissionExists(permissionUse)) permission.RegisterPermission(permissionUse, this);
if (enableCustomPerms) {
if (!permission.PermissionExists(permCustomPlayerwearable)) permission.RegisterPermission(permCustomPlayerwearable, this);
if (!permission.PermissionExists(permCustomWeapon)) permission.RegisterPermission(permCustomWeapon, this);
if (!permission.PermissionExists(permCustomDeployable)) permission.RegisterPermission(permCustomDeployable, this);
}
if (costExcludePermEnabled) {
if (!permission.PermissionExists(costExcludePerm)) permission.RegisterPermission(costExcludePerm, this);
}
skinsCache = new Dictionary<string, LinkedList<ulong>>();
skinsCacheLimited = new Dictionary<string, LinkedList<ulong>>();
approvedSkinsCount = new Dictionary<string, int>();
NameToItemName = new Dictionary<string, string>();
ItemNameToName = new Dictionary<string, string>();
checkedPlayers = new List<ulong>();
activeBoxUsers = new Dictionary<ulong, Vector3>();
skinsLoaded = false;
skinBox = this;
if (maxPagesShown < 1) maxPagesShown = 1;
maxItemsShown = (36 * maxPagesShown);
}
void Unload() {
var objs = UnityEngine.Object.FindObjectsOfType<SkinBoxHandler>().ToList();
if (objs.Count > 0) foreach (var obj in objs) {
if (obj.looter == null) continue;
obj.looter.EndLooting();
obj.PlayerStoppedLooting(obj.looter);
GameObject.Destroy(obj);
}
if (Interface.Oxide.IsShuttingDown) return;
if (_getApprovedWorkshopSkins != null) ServerMgr.Instance.StopCoroutine(_getApprovedWorkshopSkins);
if (_getRankedWorkshopSkins != null) ServerMgr.Instance.StopCoroutine(_getRankedWorkshopSkins);
if (_searchWorkshopSkin != null) ServerMgr.Instance.StopCoroutine(_searchWorkshopSkin);
if (_validateManualAddedSkins != null) ServerMgr.Instance.StopCoroutine(_validateManualAddedSkins);
}
void OnServerInitialized() {
if (enableUsageCost) {
if (ServerRewards && useServerRewards) activeServerRewards = true;
if (Economics && useEconomics) activeEconomics = true;
if (activeServerRewards && activeEconomics) activeEconomics = false;
if (activeServerRewards || activeEconomics) activePointSystem = true;
}
if (allowStackedItems && StacksExtended) {
_hasStacksExtended = true;
_stacksExtendedExtrasDisabled = (bool)StacksExtended.CallHook("DisableExtraFeatures");
if (!_stacksExtendedExtrasDisabled) {
Unsubscribe(nameof(CanStackItem));
Unsubscribe(nameof(OnItemSplit));
}
else {
Subscribe(nameof(CanStackItem));
Subscribe(nameof(OnItemSplit));
}
}
foreach (var skin in Skinnable.All.ToList()) {
if (skin.Name == null || skin.Name == string.Empty) continue;
if (skin.ItemName == null || skin.ItemName == string.Empty) continue;
if (!NameToItemName.ContainsKey(skin.Name.ToLower())) NameToItemName.Add(skin.Name.ToLower(), skin.ItemName.ToLower());
if (!ItemNameToName.ContainsKey(skin.ItemName.ToLower())) ItemNameToName.Add(skin.ItemName.ToLower(), skin.Name.ToLower());
}
foreach (var manual in manualAddedSkinsPre) manualAddedSkins.Add(manual.Key.ToString(), (manual.Value as List<object>).ConvertAll(obj => Convert.ToUInt64(obj)));
excludedSkins = excludedSkinsPre.ConvertAll(obj => Convert.ToUInt64(obj));
if (useManualAddedSkins) {
_validateManualAddedSkins = ValidateManualAddedSkins();
ServerMgr.Instance.StartCoroutine(_validateManualAddedSkins);
}
else {
isValidated = true;
GetItemSkins();
}
Initialized = true;
}
IEnumerator ValidateManualAddedSkins() {
if (showLoadedSkinCounts) Puts($"Calling workshop to validate '{manualAddedSkins.Values.Sum(list => list.Count)}' manual added skins");
int removeCount = 0;
foreach (var pair in manualAddedSkins.ToList()) {
var wsQuery = Rust.Global.SteamServer.Workshop.CreateQuery();
wsQuery.Page = 1;
wsQuery.PerPage = pair.Value.Count;
wsQuery.FileId = new List<ulong>(pair.Value);
wsQuery.Run();
yield return new WaitWhile(new System.Func<bool>(() => wsQuery.IsRunning));
foreach (var item in wsQuery.Items.ToList()) {
if (item.Title == string.Empty || item.Tags.Any(t => t.ToLower() == "version2")) {
manualAddedSkins[pair.Key].Remove(item.Id);
removeCount++;
}
}
wsQuery.Dispose();
}
if (showLoadedSkinCounts) if (removeCount > 0) {
Puts($"Removed '{removeCount}' invalids from manual skinlist");
Config["SkinsAdded", "SkinList"] = manualAddedSkins;
Config.Save();
}
else Puts($"Manual skinlist successful validated");
isValidated = true;
GetItemSkins();
yield return null;
}
void GetItemSkins() {
if (skinsLoaded) return;
int countInbuilt = 0;
foreach (var itemDef in ItemManager.GetItemDefinitions()) {
List<ulong> skins = new List<ulong> {
0
}
;
if (useInbuiltSkins) skins.AddRange(ItemSkinDirectory.ForItem(itemDef).Select(skin => Convert.ToUInt64(skin.id)));
skinsCache.Add(itemDef.shortname, new LinkedList<ulong>(skins));
if (skins.Count > 1) countInbuilt += (skins.Count - 1);
}
if (showLoadedSkinCounts && useInbuiltSkins) Puts($"Loaded {countInbuilt} inbuilt skins");
if (useManualAddedSkins) {
int countManual = 0;
foreach (var manualskins in manualAddedSkins) {
string shortname = manualskins.Key;
if (!ItemNameToName.ContainsKey(shortname)) continue;
string itemname = ItemNameToName[shortname];
List<ulong> fileids = manualskins.Value;
foreach (var fileid in fileids) {
if (!skinsCache.ContainsKey(shortname)) {
skinsCache.Add(shortname, new LinkedList<ulong>());
skinsCache[shortname].AddLast(0);
}
if (!skinsCache[shortname].Contains(fileid)) {
skinsCache[shortname].AddLast(fileid);
countManual++;
}
}
}
if (showLoadedSkinCounts && countManual > 0) Puts($"Loaded {countManual} manual added skins");
}
if (useApprovedSkins) {
_getApprovedWorkshopSkins = GetApprovedWorkshopSkins();
ServerMgr.Instance.StartCoroutine(_getApprovedWorkshopSkins);
return;
}
else if (useWebskinsRankedByTrend) {
_getRankedWorkshopSkins = GetRankedWorkshopSkins();
ServerMgr.Instance.StartCoroutine(_getRankedWorkshopSkins);
return;
}
else {
Interface.CallHook("OnSkinCacheUpdate", new Dictionary<string, LinkedList<ulong>>(skinsCache), true);
skinsLoaded = true;
}
}
object getSkincache() {
if (skinsLoaded) return skinsCache;
else return false;
}
public IEnumerator GetApprovedWorkshopSkins(bool timed = false) {
getApprovedQueries = new List<Workshop.Query>();
countQueriedGetApproved = 0;
if (usedThreadsGetRanked < 1) usedThreadsGetRanked = 1;
var waitCounter = new WaitWhile(() => getApprovedQueries.Count > 10);
if (showLoadedSkinCounts) Puts($"Checking approved skins of '{Skinnable.All.Length}' skinnables");
foreach (var pair in ItemNameToName.ToList()) {
Workshop.Query wsQuery = Rust.Global.SteamServer.Workshop.CreateQuery();
wsQuery.Page = 1;
wsQuery.PerPage = maxItemsShown;
wsQuery.RequireTags.Add("version3");
wsQuery.RequireTags.Add("skin");
wsQuery.RequireTags.Add(pair.Value.ToString());
wsQuery.RequireAllTags = true;
wsQuery.Order = Workshop.Order.AcceptedForGameRankedByAcceptanceDate;
wsQuery.OnResult = new Action<Workshop.Query>(OnApprovedWorkshopQuery);
getApprovedQueries.Add(wsQuery);
wsQuery.Run();
yield return waitCounter;
}
yield return new WaitUntil(() => getApprovedQueries.Count == 0);
if (showLoadedSkinCounts && countQueriedGetApproved > 0) Puts($"Imported {countQueriedGetApproved} approved skins for '{skinsCache.Where(c => c.Value.Count > 1).ToList().Count}' types");
if (useWebskinsRankedByTrend) {
_getRankedWorkshopSkins = GetRankedWorkshopSkins();
ServerMgr.Instance.StartCoroutine(_getRankedWorkshopSkins);
}
else {
Interface.CallHook("OnSkinCacheUpdate", new Dictionary<string, LinkedList<ulong>>(skinsCache), true);
skinsLoaded = true;
}
}
void OnApprovedWorkshopQuery(Workshop.Query query) {
if (query.Items == null) return;
foreach (var item in query.Items.Where(i => i.Title != string.Empty).ToList()) {
foreach (var tag in item.Tags.Where(t => t.ToLower() != "version3" && t.ToLower() != "skin")) {
if (NameToItemName.ContainsKey(tag)) {
skinWorkshopNames[item.Id] = item.Title;
string shortname = NameToItemName[tag];
if (!approvedSkinsCount.ContainsKey(shortname)) approvedSkinsCount[shortname] = 0;
if (approvedSkinsLimit > 0 && approvedSkinsCount[shortname] >= approvedSkinsLimit) {
if (!skinsCacheLimited.ContainsKey(shortname)) skinsCacheLimited[shortname] = new LinkedList<ulong>();
skinsCacheLimited[shortname].AddLast(item.Id);
}
if (skinsCacheLimited.ContainsKey(shortname) && skinsCacheLimited[shortname].Contains(item.Id)) continue;
if (!skinsCache.ContainsKey(shortname)) skinsCache[shortname] = new LinkedList<ulong>();
if (!skinsCache[shortname].Contains(item.Id) && skinsCache[shortname].Count < maxItemsShown) {
skinsCache[shortname].AddLast(item.Id);
approvedSkinsCount[shortname]++;
countQueriedGetApproved++;
}
}
}
}
getApprovedQueries.Remove(query);
query.Dispose();
}
IEnumerator GetRankedWorkshopSkins() {
getRankedQueries = new List<Workshop.Query>();
countQueriedGetRanked = 0;
if (usedThreadsGetRanked < 1) usedThreadsGetRanked = 1;
var waitCounter = new WaitWhile(() => getRankedQueries.Count > usedThreadsGetRanked);
if (showLoadedSkinCounts) Puts($"Filling up the Cache by '{usedThreadsGetRanked}' workshop threads");
foreach (var pair in ItemNameToName.ToList()) {
Workshop.Query wsQuery = Rust.Global.SteamServer.Workshop.CreateQuery();
wsQuery.Page = 1;
wsQuery.PerPage = maxItemsShown;
wsQuery.ExcludeTags.Add("version2");
wsQuery.RequireTags.Add("version3");
wsQuery.RequireTags.Add("skin");
wsQuery.RequireTags.Add(pair.Value.ToString());
wsQuery.RequireAllTags = true;
wsQuery.Order = Workshop.Order.RankedByTrend;
wsQuery.RankedByTrendDays = usedRankedByTrendDays;
wsQuery.OnResult = new Action<Workshop.Query>(OnRankedWorkshopQuery);
getRankedQueries.Add(wsQuery);
wsQuery.Run();
yield return waitCounter;
}
yield return new WaitUntil(() => getRankedQueries.Count == 0);
if (showLoadedSkinCounts && countQueriedGetRanked > 0) Puts($"Cache for {ItemNameToName.Count} Skinnables filled with {countQueriedGetRanked} ranked skins");
Interface.CallHook("OnSkinCacheUpdate", new Dictionary<string, LinkedList<ulong>>(skinsCache), true);
skinsLoaded = true;
}
void OnRankedWorkshopQuery(Workshop.Query query) {
if (query.Items == null) return;
foreach (var item in query.Items.Where(i => i.Title != string.Empty).ToList()) {
foreach (var tag in item.Tags.Where(t => t.ToLower() != "version3" && t.ToLower() != "skin")) {
if (NameToItemName.ContainsKey(tag)) {
skinWorkshopNames[item.Id] = item.Title;
string shortname = NameToItemName[tag];
if (!skinsCache.ContainsKey(shortname)) continue;
if (!skinsCache[shortname].Contains(item.Id) && skinsCache[shortname].Count < maxItemsShown) {
skinsCache[shortname].AddLast(item.Id);
countQueriedGetRanked++;
}
}
}
}
getRankedQueries.Remove(query);
query.Dispose();
}
[ConsoleCommand("skinbox.addexcluded")]
void consoleAddExcluded(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in one or more Workshop FileId's");
return;
}
List<ulong> fileIds = new List<ulong>();
for (int i = 0; i < arg.Args.Length; i++) {
ulong fileId = 0uL;
if (!ulong.TryParse(arg.Args[i], out fileId)) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not a number");
continue;
}
else {
if (arg.Args[i].Length < 9 || arg.Args[i].Length > 10) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9/10-Digits");
continue;
}
fileIds.Add(fileId);
}
}
int countAdded = 0;
foreach (var fileId in fileIds) if (!excludedSkins.Contains(fileId)) {
excludedSkins.Add(fileId);
countAdded++;
}
if (countAdded > 0) {
Config["SkinsExcluded", "SkinList"] = excludedSkins;
Config.Save();
SendReply(arg, $"Added {countAdded} skins to exclusion list");
}
}
[ConsoleCommand("skinbox.removeexcluded")]
void consoleRemoveExcluded(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in one or more Workshop FileId's");
return;
}
List<ulong> fileIds = new List<ulong>();
for (int i = 0; i < arg.Args.Length; i++) {
ulong fileId = 0uL;
if (!ulong.TryParse(arg.Args[i], out fileId)) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not a number");
continue;
}
else {
if (arg.Args[i].Length < 9 || arg.Args[i].Length > 10) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9/10-Digits");
continue;
}
fileIds.Add(fileId);
}
}
int countRemoved = 0;
foreach (var fileId in fileIds) if (excludedSkins.Contains(fileId)) {
excludedSkins.Remove(fileId);
countRemoved++;
}
if (countRemoved > 0) {
Config["SkinsExcluded", "SkinList"] = excludedSkins;
Config.Save();
SendReplyCl(arg, $"Removed {countRemoved} skins from exclusion");
}
}
[ConsoleCommand("skinbox.addskin")]
void consoleAddSkin(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in one or more Workshop FileId's");
return;
}
List<ulong> fileIds = new List<ulong>();
for (int i = 0; i < arg.Args.Length; i++) {
ulong fileId = 0uL;
if (!ulong.TryParse(arg.Args[i], out fileId)) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not a number");
continue;
}
else {
if (arg.Args[i].Length < 9 || arg.Args[i].Length > 10) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9/10-Digits");
continue;
}
fileIds.Add(fileId);
}
}
ServerMgr.Instance.StartCoroutine(SearchWorkshopSkin(fileIds, arg));
}
[ConsoleCommand("skinbox.removeskin")]
void consoleRemoveSkin(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in one or more Workshop FileId's");
return;
}
List<ulong> fileIds = new List<ulong>();
for (int i = 0; i < arg.Args.Length; i++) {
ulong fileId = 0uL;
if (!ulong.TryParse(arg.Args[i], out fileId)) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not a number");
continue;
}
else {
if (arg.Args[i].Length < 9 || arg.Args[i].Length > 10) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9/10-Digits");
continue;
}
fileIds.Add(fileId);
}
}
bool doSave = false;
int removed = 0;
foreach (var addedSkins in manualAddedSkins) {
foreach (var fileId in fileIds) {
if (addedSkins.Value.Contains(fileId)) {
manualAddedSkins[addedSkins.Key].Remove(fileId);
skinsCache[addedSkins.Key].Remove(fileId);
removed++;
doSave = true;
}
}
}
if (doSave) {
Config["SkinsAdded", "SkinList"] = manualAddedSkins;
Config.Save();
SendReply(arg, $"Removed {removed} FileId's");
Interface.CallHook("OnSkinCacheUpdate", new Dictionary<string, LinkedList<ulong>>(skinsCache), true);
}
}
public IEnumerator SearchWorkshopSkin(List<ulong> fileIds, ConsoleSystem.Arg arg = null) {
var wsQuery = Rust.Global.SteamServer.Workshop.CreateQuery();
wsQuery.Page = 1;
wsQuery.PerPage = 1;
wsQuery.RequireTags.Add("version3");
wsQuery.RequireTags.Add("version2");
wsQuery.FileId = new List<ulong>(fileIds);
SendReplyCl(arg, $"Calling Workshop for '{fileIds.Count}' FileId's");
wsQuery.Run();
yield return new WaitWhile(new System.Func<bool>(() => wsQuery.IsRunning));
bool doSave = false;
foreach (var item in wsQuery.Items.Where(i => i.Title != string.Empty).ToList()) {
if (item.Tags.Any(t => t.ToLower() == "version2")) {
SendReplyCl(arg, $"Version2 skins are not more supported in the game ({item.Id})'");
continue;
}
bool matchedAny = false;
foreach (var tag in item.Tags.Where(t => t.ToLower() != "version3" && t.ToLower() != "skin")) {
if (NameToItemName.ContainsKey(tag)) {
string shortname = NameToItemName[tag];
if (manualAddedSkins.ContainsKey(shortname)) {
if (manualAddedSkins[shortname].Contains(item.Id)) {
SendReplyCl(arg, $"'{item.Title} ({item.Id})' was already added");
matchedAny = true;
continue;
}
}
if (skinsCache.ContainsKey(shortname)) if ((skinsCache[shortname] as LinkedList<ulong>).Contains(item.Id)) {
SendReplyCl(arg, $"'{item.Title} ({item.Id})' belongs already to approved/ranked");
matchedAny = true;
continue;
}
if (!manualAddedSkins.ContainsKey(shortname)) manualAddedSkins.Add(shortname, new List<ulong>());
manualAddedSkins[shortname].Add(item.Id);
if (!skinsCache.ContainsKey(shortname)) {
skinsCache.Add(shortname, new LinkedList<ulong>());
skinsCache[shortname].AddLast(0);
}
skinsCache[shortname].AddAfter(skinsCache[shortname].First, item.Id);
skinWorkshopNames[item.Id] = item.Title;
SendReplyCl(arg, $"'{item.Title} ({item.Id})' added to the list for '{shortname}'");
matchedAny = true;
doSave = true;
}
}
if (!matchedAny) SendReplyCl(arg, $"No shortname found for '{item.Title}|{item.Id}'.Fix it inside 'MissingSkinNames'");
}
if (doSave) {
Config["SkinsAdded", "SkinList"] = manualAddedSkins;
Config.Save();
Interface.CallHook("OnSkinCacheUpdate", new Dictionary<string, LinkedList<ulong>>(skinsCache), true);
}
wsQuery.Dispose();
}
[ConsoleCommand("skinbox.addcollection")]
void consoleAddCollection(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (steamApiKey == null || steamApiKey == string.Empty) {
SendReply(arg, "This functions needs valid defined steam api-key");
return;
}
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in a valid collection id");
return;
}
ulong collId = 0;
if (!ulong.TryParse(arg.Args[0], out collId)) {
SendReply(arg, $"Collection ID not correct: '{arg.Args[0]}' is not a number");
return;
}
else {
if (arg.Args[0].Length < 9 || arg.Args[0].Length > 10) {
SendReply(arg, $"Collection ID not correct: '{arg.Args[0]}' has not 9/10-Digits");
return;
}
}
var url = $"https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/";
var body = $"?key={steamApiKey}&collectioncount=1&publishedfileids[0]={arg.Args[0]}";
try {
webrequest.Enqueue(url, body, (code, response) => PostCallbackAdd(code, response, arg), this, RequestMethod.POST);
}
catch {
SendReplyCl(arg, "Steam webrequest failed!");
}
}
void PostCallbackAdd(int code, string response, ConsoleSystem.Arg arg = null) {
if (response == null || code != 200) {
SendReplyCl(arg, "Steam webrequest failed by wrong response!");
return;
}
var col = JsonConvert.DeserializeObject<GetCollectionDetails>(response);
if (col == null || !(col is GetCollectionDetails)) {
SendReplyCl(arg, "No Collection data received!");
return;
}
if (col.response.resultcount == 0 || col.response.collectiondetails == null || col.response.collectiondetails.Count == 0 || col.response.collectiondetails[0].result != 1) {
SendReplyCl(arg, "The Steam collection could not be found!");
return;
}
List<ulong> fileIds = new List<ulong>();
foreach (var child in col.response.collectiondetails[0].children) {
try {
fileIds.Add(Convert.ToUInt64(child.publishedfileid));
}
catch {
}
}
if (fileIds.Count == 0) {
SendReplyCl(arg, "No skin numbers found.Workshop search cancelled.");
return;
}
if (_searchWorkshopSkin != null) ServerMgr.Instance.StopCoroutine(_searchWorkshopSkin);
_searchWorkshopSkin = SearchWorkshopSkin(fileIds, arg);
ServerMgr.Instance.StartCoroutine(_searchWorkshopSkin);
}
[ConsoleCommand("skinbox.removecollection")]
void consoleRemoveCollection(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (steamApiKey == null || steamApiKey == string.Empty) {
SendReply(arg, "This functions needs valid defined steam api-key");
return;
}
if (arg.Args == null || arg.Args.Length < 1) {
SendReply(arg, "You need to type in a valid collection id");
return;
}
ulong collId = 0;
if (!ulong.TryParse(arg.Args[0], out collId)) {
SendReply(arg, $"Collection ID not correct: '{arg.Args[0]}' is not a number");
return;
}
else {
if (arg.Args[0].Length < 9 || arg.Args[0].Length > 10) {
SendReply(arg, $"Collection ID not correct: '{arg.Args[0]}' has not 9/10-Digits");
return;
}
}
var url = $"https://api.steampowered.com/ISteamRemoteStorage/GetCollectionDetails/v1/";
var body = $"?key={steamApiKey}&collectioncount=1&publishedfileids[0]={arg.Args[0]}";
try {
webrequest.Enqueue(url, body, (code, response) => PostCallbackRemove(code, response, arg), this, RequestMethod.POST);
}
catch {
SendReplyCl(arg, "Steam webrequest failed!");
}
}
void PostCallbackRemove(int code, string response, ConsoleSystem.Arg arg = null) {
if (response == null || code != 200) {
SendReplyCl(arg, "Steam webrequest failed by wrong response!");
return;
}
var col = JsonConvert.DeserializeObject<GetCollectionDetails>(response);
if (col == null || !(col is GetCollectionDetails)) {
SendReplyCl(arg, "No Collection data received!");
return;
}
if (col.response.resultcount == 0 || col.response.collectiondetails == null || col.response.collectiondetails.Count == 0 || col.response.collectiondetails[0].result != 1) {
SendReplyCl(arg, "The Steam collection could not be found!");
return;
}
List<ulong> fileIds = new List<ulong>();
foreach (var child in col.response.collectiondetails[0].children) {
try {
fileIds.Add(Convert.ToUInt64(child.publishedfileid));
}
catch {
}
}
if (fileIds.Count == 0) {
SendReplyCl(arg, "No skin numbers found.Workshop search cancelled.");
return;
}
int removed = 0;
foreach (var addSkins in new Dictionary<string, List<ulong>>(manualAddedSkins)) {
foreach (var skin in addSkins.Value.ToList()) {
if (fileIds.Contains(skin)) {
manualAddedSkins[addSkins.Key].Remove(skin);
if (skinsCache.ContainsKey(addSkins.Key)) skinsCache[addSkins.Key].Remove(skin);
removed++;
}
}
}
if (removed > 0) {
SendReplyCl(arg, $"Removed '{removed}' manual skins by collection remove.");
Config["SkinsAdded", "SkinList"] = manualAddedSkins;
Config.Save();
}
else SendReplyCl(arg, $"No manual skins to remove by collection remove.");
}
public class GetCollectionDetails {
[JsonProperty("response")]
public Response response;
public class Response {
[JsonProperty("result")]
public int result;
[JsonProperty("resultcount")]
public int resultcount;
[JsonProperty("collectiondetails")]
public List<Collectiondetail> collectiondetails;
public class Collectiondetail {
[JsonProperty("publishedfileid")]
public string publishedfileid;
[JsonProperty("result")]
public int result;
[JsonProperty("children")]
public List<Child> children;
public class Child {
[JsonProperty("publishedfileid")]
public string publishedfileid;
[JsonProperty("sortorder")]
public int sortorder;
[JsonProperty("filetype")]
public int filetype;
}
}
}
}
void OnPlayerInit(BasePlayer player) {
if (player == null) return;
if (!forceClientPreload || (checkedPlayers.Contains(player.userID) && !forceClientAlways)) return;
timer.Once(0.1f, () => InitPreload(player));
}
void InitPreload(BasePlayer player) {
ItemContainer beltInv = player.inventory.containerBelt;
ItemContainer wearInv = player.inventory.containerWear;
var wearList = new List<Item>();
if (forceAlsoWearables) {
foreach (var item in wearInv.itemList.ToList()) {
wearList.Add(item);
item.RemoveFromContainer();
}
player.inventory.SendUpdatedInventory(PlayerInventory.Type.Wear, wearInv, false);
}
beltInv.capacity++;
foreach (var skinCache in skinsCache) {
if ((skinCache.Value as LinkedList<ulong>).Count <= 1) continue;
foreach (var skin in (LinkedList<ulong>)skinCache.Value) {
if (skin == 0) continue;
var itemDef = ItemManager.FindItemDefinition(skinCache.Key);
if (itemDef == null) continue;
Item item = ItemManager.Create(itemDef, 1, skin);
if (item == null || !item.IsValid()) continue;
if (item.info.category == ItemCategory.Attire && !forceAlsoWearables) {
item.Remove(0f);
continue;
}
if (item.info.category == ItemCategory.Attire && forceAlsoWearables) {
item.MoveToContainer(wearInv);
player.inventory.SendUpdatedInventory(PlayerInventory.Type.Wear, wearInv, false);
}
else {
item.MoveToContainer(beltInv, 6, false);
player.inventory.SendUpdatedInventory(PlayerInventory.Type.Belt, beltInv, false);
}
item.RemoveFromContainer();
item.Remove(0f);
}
}
beltInv.capacity--;
if (forceAlsoWearables) {
foreach (var item in wearList) item.MoveToContainer(wearInv);
player.inventory.SendUpdatedInventory(PlayerInventory.Type.Wear, wearInv, false);
wearList.Clear();
}
if (!checkedPlayers.Contains(player.userID)) checkedPlayers.Add(player.userID);
ItemManager.DoRemoves();
}
[ConsoleCommand("skinbox.open")]
void consoleSkinboxOpen(ConsoleSystem.Arg arg) {
if (arg == null || !isValidated) return;
if (arg.Connection == null) {
if (arg.Args == null || arg.Args.Length == 0) {
SendReply(arg, $"'skinbox.open' cmd needs a passed steamid ");
return;
}
ulong argId = 0uL;
if (!ulong.TryParse(arg.Args[0], out argId)) {
SendReply(arg, $"'skinbox.open' cmd for '{arg.Args[0]}' failed (no valid number)");
return;
}
BasePlayer argPlayer = BasePlayer.FindByID(argId);
if (argPlayer == null) {
SendReply(arg, $"'skinbox.open' cmd for userID '{argId}' failed (player not found)");
return;
}
if (!argPlayer.inventory.loot.IsLooting()) OpenSkinBox(argPlayer);
}
else if (arg.Connection != null && arg.Connection.player != null) {
BasePlayer player = arg.Player();
if (player.inventory.loot.IsLooting()) return;
if (!(player.IsAdmin || player.net.connection.authLevel >= accessOverrideAuthLevel) && !permission.UserHasPermission(player.UserIDString, permissionUse)) {
player.ChatMessage(lang.GetMessage("NoPermission", this, player.UserIDString));
return;
}
if (!CheckDistance(player) || !CheckOpenBalance(player)) return;
if (enableCooldown && !(cooldownOverrideAdmin && (player.IsAdmin || player.net.connection.authLevel >= cooldownOverrideAuthLevel))) {
DateTime now = DateTime.UtcNow;
DateTime time;
var key = player.UserIDString + "-box";
if (cooldownTimes.TryGetValue(key, out time)) {
if (time > now.AddSeconds(-cooldownBox)) {
player.ChatMessage(string.Format(lang.GetMessage("CooldownTime", this, player.UserIDString), (time - now.AddSeconds(-cooldownBox)).Seconds));
return;
}
}
}
OpenSkinBox(player);
}
}
void cmdSkinBox(BasePlayer player, string command, string[] args) {
if (player.inventory.loot.IsLooting() || !isValidated)
// ... [Code preview truncated due to length. Full source in attached file] ...