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 Rust;
using Oxide.Core;
using Oxide.Core.Plugins;
namespace Oxide.Plugins {
[Info("SkinBox", "Fujikura", "1.8.3", ResourceId=17)]
class SkinBox : RustPlugin {
[PluginReference]
Plugin QuickSort;
[PluginReference]
Plugin ServerRewards;
[PluginReference]
Plugin Economics;
bool skinsLoaded;
bool Changed=false;
bool _unloaded;
static SkinBox skinBox=null;
bool activeServerRewards;
bool activeEconomics;
bool activePointSystem;
Dictionary<string, List<ulong>>skinsCache=new Dictionary<string, List<ulong>>();
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>();
static Dictionary<string, object>missingWorkshopNames() {
var dp=new Dictionary<string, object>();
dp.Add("rug", "Rug");
dp.Add("chair", "Chair");
dp.Add("rug.bear", "Bearskin Rug");
dp.Add("bandana", "Bandana");
dp.Add("table", "Table");
return dp;
}
string skinBoxCommand;
string permissionUse;
bool forceClientPreload;
bool forceClientAlways;
bool forceAlsoWearables;
bool showLoadedSkinCounts;
int exludedSkinsAuthLevel;
bool hideQuickSort;
float boxUseDistance;
bool enableCustomPerms;
string permCustomPlayerwearable;
string permCustomWeapon;
string permCustomDeployable;
bool useInbuiltSkins;
bool useApprovedSkins;
bool useManualAddedSkins;
bool useWebskinsRankedByTrend;
int usedRankedByTrendDays;
Dictionary<string, object>missingSkinNames=new Dictionary<string, object>();
bool enableCooldown;
int cooldownBox;
bool cooldownOverrideAdmin;
bool activateAfterSkinTaken;
bool useKeyBindLogin;
string keyToBind;
bool enableUsageCost;
bool useServerRewards;
bool useEconomics;
int costBoxOpen;
int costWeapon;
int costPlayerwearable;
int costDeployable;
bool costExcludeAdmins;
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));
useManualAddedSkins=Convert.ToBoolean(GetConfig("AvailableSkins", "useManualAddedSkins", true));
useWebskinsRankedByTrend=Convert.ToBoolean(GetConfig("AvailableSkins", "useWebskinsRankedByTrend", false));
usedRankedByTrendDays=Convert.ToInt32(GetConfig("AvailableSkins", "usedRankedByTrendDays", 30));
missingSkinNames=(Dictionary<string, object>)GetConfig("AvailableSkins", "MissingSkinNames", missingWorkshopNames());
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));
hideQuickSort=Convert.ToBoolean(GetConfig("Settings", "hideQuickSort", false));
boxUseDistance=Convert.ToSingle(GetConfig("Settings", "boxUseDistance", 10.0));
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));
activateAfterSkinTaken=Convert.ToBoolean(GetConfig("Cooldown", "activateAfterSkinTaken", true));
useKeyBindLogin=Convert.ToBoolean(GetConfig("KeyBind", "useKeyBindLogin", false));
keyToBind=Convert.ToString(GetConfig("KeyBind", "keyToBind", "c"));
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));
if (!Changed) return;
SaveConfig();
Changed=false;
}
protected override void LoadDefaultConfig() {
Config.Clear();
LoadVariables();
}
void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
{
"NoPermission", "You don't have permission to use the SkinBox"
}
, {
"ToNearPlayer", "The SkinBox is currently not usable at this place"
}
, {
"CooldownTime", "You need to wait {0} seconds to re-open the SkinBox again"
}
, {
"NotEnoughBalanceOpen", "You need at least '{0}' bucks to open the SkinBox"
}
, {
"NotEnoughBalanceUse", "You would need at least '{0}' bucks to skin '{1}'"
}
, {
"NotEnoughBalanceTake", "'{0}' was not skinned.You had not enough bucks"
}
,
}
, this);
}
void Loaded() {
LoadVariables();
LoadDefaultMessages();
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);
}
skinsCache=new Dictionary<string, List<ulong>>();
NameToItemName=new Dictionary<string, string>();
ItemNameToName=new Dictionary<string, string>();
checkedPlayers=new List <ulong>();
activeBoxUsers=new Dictionary <ulong, Vector3>();
skinsLoaded=false;
_unloaded=false;
skinBox=this;
}
void OnServerInitialized() {
if (enableUsageCost) {
if (ServerRewards && useServerRewards) activeServerRewards=true;
if (Economics && useEconomics) activeEconomics=true;
if (activeServerRewards && activeEconomics) activeEconomics=false;
if (activeServerRewards || activeEconomics) activePointSystem=true;
}
OnServerInit();
}
void OnServerInit() {
foreach (var itemtype in Rust.Workshop.Approved.All.ToList()) {
if (itemtype.Skinnable.Name==null || itemtype.Skinnable.Name==string.Empty) continue;
if (itemtype.Skinnable.ItemName==null || itemtype.Skinnable.ItemName==string.Empty) continue;
if (!NameToItemName.ContainsKey(itemtype.Skinnable.Name.ToLower())) NameToItemName.Add(itemtype.Skinnable.Name.ToLower(), itemtype.Skinnable.ItemName.ToLower());
if (!ItemNameToName.ContainsKey(itemtype.Skinnable.ItemName.ToLower())) ItemNameToName.Add(itemtype.Skinnable.ItemName.ToLower(), itemtype.Skinnable.Name.ToLower());
}
foreach (var skin in missingSkinNames) {
var itemname=skin.Key.ToLower();
var itemDef=ItemManager.FindItemDefinition(itemname);
if (itemDef==null) continue;
var workshopname=((string)skin.Value).ToLower();
if (!NameToItemName.ContainsKey(workshopname)) NameToItemName.Add(workshopname, itemname);
if (!ItemNameToName.ContainsKey(itemname)) ItemNameToName.Add(itemname, workshopname);
}
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));
GetItemSkins();
}
void Unload() {
_unloaded=true;
ServerMgr.Instance.StopCoroutine("GetRankedWorkshopSkins");
ServerMgr.Instance.StopCoroutine("GetAcceptedWorkshopSkins");
ServerMgr.Instance.StopCoroutine("SearchWorkshopSkin");
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);
}
}
void GetItemSkins() {
if (skinsLoaded) return;
int countInbuilt=0;
foreach (var itemDef in ItemManager.GetItemDefinitions()) {
List<ulong>skins;
skins=new List<ulong> {
0
}
;
if (useInbuiltSkins) skins.AddRange(ItemSkinDirectory.ForItem(itemDef).Select(skin=> Convert.ToUInt64(skin.id)));
skinsCache.Add(itemDef.shortname, skins);
if (skins.Count > 1) countInbuilt+=(skins.Count -1);
}
if (showLoadedSkinCounts && useInbuiltSkins) {
Puts($"Loaded {countInbuilt} inbuilt skins");
Interface.CallHook("OnSkinCacheUpdate", skinsCache);
}
if (useApprovedSkins) {
int countApproved=0;
foreach (var shopskin in Rust.Workshop.Approved.All.Where(skin=> skin.Skinnable.ItemName !=null)) {
if (!skinsCache.ContainsKey(shopskin.Skinnable.ItemName)) skinsCache[shopskin.Skinnable.ItemName]=new List<ulong>();
if (!skinsCache[shopskin.Skinnable.ItemName].Contains(shopskin.WorkshopdId)) {
skinsCache[shopskin.Skinnable.ItemName].Add(shopskin.WorkshopdId);
countApproved++;
}
}
if (showLoadedSkinCounts) Puts($"Loaded {countApproved} inbuilt approved Workshop 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[shortname].Contains(fileid)) {
skinsCache[shortname].Add(fileid);
countManual++;
}
}
}
if (showLoadedSkinCounts && countManual > 0) {
Puts($"Loaded {countManual} manual added skins");
Interface.CallHook("OnSkinCacheUpdate", skinsCache);
}
}
skinsLoaded=true;
if (useApprovedSkins) ServerMgr.Instance.StartCoroutine(GetAcceptedWorkshopSkins());
else if (useWebskinsRankedByTrend) ServerMgr.Instance.StartCoroutine(GetRankedWorkshopSkins());
}
void RunQuery(Facepunch.Steamworks.Workshop.Query query) {
FieldInfo Callback=typeof(Facepunch.Steamworks.Workshop.Query).GetField("Callback", (BindingFlags.Instance | BindingFlags.NonPublic));
FieldInfo _resultSkip=typeof(Facepunch.Steamworks.Workshop.Query).GetField("_resultSkip", (BindingFlags.Instance | BindingFlags.NonPublic));
FieldInfo _resultsRemain=typeof(Facepunch.Steamworks.Workshop.Query).GetField("_resultsRemain", (BindingFlags.Instance | BindingFlags.NonPublic));
FieldInfo _resultPage=typeof(Facepunch.Steamworks.Workshop.Query).GetField("_resultPage", (BindingFlags.Instance | BindingFlags.NonPublic));
FieldInfo _results=typeof(Facepunch.Steamworks.Workshop.Query).GetField("_results", (BindingFlags.Instance | BindingFlags.NonPublic));
MethodInfo runInternal=typeof(Facepunch.Steamworks.Workshop.Query).GetMethod("RunInternal", BindingFlags.NonPublic | BindingFlags.Instance);
bool flag=Callback.GetValue(query) !=null;
if (!flag) {
bool flag2=query.Page <=0;
if (flag2) {
PrintWarning("Page should be 1 or above");
return;
}
int num=(query.Page - 1) * query.PerPage;
query.TotalResults=0;
_resultSkip.SetValue(query, num % 50);
_resultsRemain.SetValue(query, query.PerPage);
_resultPage.SetValue(query, (int)Math.Floor((double)((float)num / 50f)));
_results.SetValue(query, new List<Workshop.Item>());
runInternal.Invoke(query, null);
}
}
public IEnumerator GetAcceptedWorkshopSkins(bool timed=false) {
yield return new WaitForSeconds (2);
var workshopQuery=Global.SteamServer.Workshop.CreateQuery();
workshopQuery.Page=1;
workshopQuery.PerPage=500;
workshopQuery.RequireTags.Add("Version3");
workshopQuery.RequireTags.Add("Skin");
workshopQuery.RequireAllTags=true;
workshopQuery.Order=Workshop.Order.AcceptedForGameRankedByAcceptanceDate;
if (showLoadedSkinCounts && !timed) Puts($"Checking Workshop for new accepted skins");
RunQuery(workshopQuery);
yield return new WaitWhile(new System.Func<bool>(()=> workshopQuery.IsRunning));
if (_unloaded) {
workshopQuery.Dispose();
workshopQuery=null;
yield return null;
}
int countQueried=0;
foreach (var item in workshopQuery.Items) {
foreach (var tag in item.Tags.Where(t=> !t.Contains("Version") && !t.Contains("Skin"))) {
if (NameToItemName.ContainsKey(tag)) {
string shortname=NameToItemName[tag];
if (!skinsCache[shortname].Contains(item.Id)) {
skinsCache[shortname].Add(item.Id);
countQueried++;
}
}
}
}
workshopQuery.Dispose();
if (showLoadedSkinCounts && countQueried > 0) {
Puts($"Loaded {countQueried} new accepted Workshop skins");
Interface.CallHook("OnSkinCacheUpdate", skinsCache);
}
else if (showLoadedSkinCounts && countQueried==0 && !timed) Puts($"No recently accepted Workshop skins found");
if (useWebskinsRankedByTrend) ServerMgr.Instance.StartCoroutine(GetRankedWorkshopSkins());
}
public IEnumerator GetRankedWorkshopSkins() {
var workshopQuery=Global.SteamServer.Workshop.CreateQuery();
workshopQuery.Page=1;
workshopQuery.PerPage=50000;
workshopQuery.RequireTags.Add("Version3");
workshopQuery.RequireTags.Add("Skin");
workshopQuery.RequireAllTags=true;
workshopQuery.Order=Workshop.Order.RankedByTrend;
workshopQuery.RankedByTrendDays=usedRankedByTrendDays;
if (showLoadedSkinCounts) Puts($"Calling Workshop to fill up the SkinCache.This can take a few moments...");
RunQuery(workshopQuery);
yield return new WaitWhile(new System.Func<bool>(()=> workshopQuery.IsRunning));
if (_unloaded) {
workshopQuery.Dispose();
yield break;
}
int countQueried=0;
foreach (var item in workshopQuery.Items) {
foreach (var tag in item.Tags.Where(t=> !t.Contains("Version") && !t.Contains("Skin"))) {
if (NameToItemName.ContainsKey(tag)) {
string shortname=NameToItemName[tag];
if (!skinsCache[shortname].Contains(item.Id) && skinsCache[shortname].Count < 31) {
skinsCache[shortname].Add(item.Id);
countQueried++;
}
}
}
}
workshopQuery.Dispose();
if (showLoadedSkinCounts && countQueried > 0) {
Puts($"Filled up SkinCache with {countQueried} new Workshop skins");
Interface.CallHook("OnSkinCacheUpdate", skinsCache);
}
}
[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) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9-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) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9-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"]=manualAddedSkins;
Config.Save();
Puts($"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) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9-Digits");
continue;
}
fileIds.Add(fileId);
}
}
ServerMgr.Instance.StartCoroutine(SearchWorkshopSkin(fileIds));
}
[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) {
SendReply(arg, $"Ignored '{arg.Args[i]}' as of not 9-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();
Puts($"Removed {removed} FileId's");
}
}
public IEnumerator SearchWorkshopSkin(List<ulong> fileIds) {
var workshopQuery=Global.SteamServer.Workshop.CreateQuery();
workshopQuery.Page=1;
workshopQuery.PerPage=1;
workshopQuery.RequireTags.Add("Version3");
workshopQuery.FileId=new List<ulong>(fileIds);
Puts($"Calling Workshop for '{fileIds.Count}' FileId's");
RunQuery(workshopQuery);
yield return new WaitWhile(new System.Func<bool>(()=> workshopQuery.IsRunning));
bool doSave=false;
foreach (var item in workshopQuery.Items) {
bool matchedAny=false;
foreach (var tag in item.Tags.Where(t=> !t.Contains("Version") && !t.Contains("Skin") && !t.Contains("Weapon") && !t.Contains("Playerwearable") && !t.Contains("Deployable"))) {
if (NameToItemName.ContainsKey(tag)) {
string shortname=NameToItemName[tag];
if (manualAddedSkins.ContainsKey(shortname)) {
if (manualAddedSkins[shortname].Contains(item.Id)) {
Puts($"The FileId '{item.Id}' was already added");
matchedAny=true;
continue;
}
}
if (skinsCache.ContainsKey(shortname)) if ((skinsCache[shortname] as List<ulong>).Contains(item.Id)) {
Puts($"The FileId '{item.Id}' belongs already to the default loaded skins");
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 List<ulong>());
skinsCache[shortname].Add(item.Id);
Puts($"Added '{item.Title}' to the manual skins list for '{shortname}'");
matchedAny=true;
doSave=true;
}
}
if (!matchedAny) Puts($"No matching item shortname found to assign the skin '{item.Id}'");
}
if (doSave) {
Config["SkinsAdded", "SkinList"]=manualAddedSkins;
Config.Save();
Interface.CallHook("OnSkinCacheUpdate", skinsCache);
}
workshopQuery.Dispose();
}
void OnPlayerInit(BasePlayer player) {
if (player==null) return;
if(useKeyBindLogin) {
player.Command($"bind {keyToBind} skinbox.open");
}
if (player==null || !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 List<ulong>).Count <=1) continue;
foreach (var skin in (List<ulong>)skinCache.Value) {
if (skin==0) continue;
Item item=ItemManager.Create(ItemManager.FindItemDefinition(skinCache.Key), 1, skin);
if (item.info.category==ItemCategory.Attire && !forceAlsoWearables) {
item.Remove(0f);
continue;
}
if (item !=null) {
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 (!useKeyBindLogin) return;
if(arg !=null && arg.Connection !=null && arg.Connection.player !=null) {
BasePlayer player=arg.Player();
if (player.inventory.loot.IsLooting()) return;
if(!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)) {
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()) return;
if(!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)) {
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;
}
}
}
timer.Once(0.1f, ()=> {
OpenSkinBox(player);
}
);
}
Boolean CheckOpenBalance(BasePlayer player) {
if (!activePointSystem || costBoxOpen <=0 || (player.IsAdmin && costExcludeAdmins)) return true;
object getMoney=null;
if (activeServerRewards) getMoney=ServerRewards?.Call("CheckPoints", player.userID);
if (activeEconomics) getMoney=Economics?.CallHook("GetPlayerMoney", player.userID);
int playerMoney=0;
playerMoney=Convert.ToInt32(getMoney);
if (playerMoney < costBoxOpen) {
player.ChatMessage(string.Format(lang.GetMessage("NotEnoughBalanceOpen", this, player.UserIDString), costBoxOpen));
return false;
}
if (activeServerRewards) ServerRewards?.Call("TakePoints", player.userID, costBoxOpen);
if (activeEconomics) Economics?.CallHook("Withdraw", player.userID, Convert.ToDouble(costBoxOpen));
return true;
}
Boolean CheckSkinBalance(BasePlayer player, Item item) {
if (!activePointSystem) return true;
object getMoney=null;
if (activeServerRewards) getMoney=ServerRewards?.Call("CheckPoints", player.userID);
if (activeEconomics) getMoney=Economics?.CallHook("GetPlayerMoney", player.userID);
int playerMoney=0;
playerMoney=Convert.ToInt32(getMoney);
bool hasBalance=false;
int getCost=0;
switch (item.info.category.ToString()) {
case "Weapon" : case "Tool" : if (costWeapon <=0 || playerMoney > costWeapon) hasBalance=true;
getCost=costWeapon;
break;
case "Attire" : if (costPlayerwearable <=0 || playerMoney > costPlayerwearable) hasBalance=true;
getCost=costPlayerwearable;
break;
case "Items" : case "Construction" : if (costDeployable <=0 || playerMoney > costDeployable) hasBalance=true;
getCost=costDeployable;
break;
default : hasBalance=true;
break;
}
if (!hasBalance) {
player.ChatMessage(string.Format(lang.GetMessage("NotEnoughBalanceUse", this, player.UserIDString), getCost, item.info.displayName.translated));
return false;
}
return true;
}
Boolean WithdrawBalance(BasePlayer player, Item item) {
if (!activePointSystem) return true;
int getCost=0;
switch (item.info.category.ToString()) {
case "Weapon" : case "Tool" : getCost=costWeapon;
break;
case "Attire" : getCost=costPlayerwearable;
break;
case "Items" : case "Construction" : getCost=costDeployable;
break;
default : break;
}
bool hadMoney=false;
if (activeServerRewards && (bool)ServerRewards?.Call("TakePoints", player.userID, getCost)) hadMoney=true;
if (activeEconomics && (bool)Economics?.CallHook("Withdraw", player.userID, Convert.ToDouble(getCost))) hadMoney=true;
if (!hadMoney) {
player.ChatMessage(string.Format(lang.GetMessage("NotEnoughBalanceTake", this, player.UserIDString), item.info.displayName.translated));
return false;
}
return true;
}
sealed class SkinBoxHandler : MonoBehaviour {
public bool isCreating;
public bool isBlocked;
public bool isCleaning;
public bool isEmptied;
public int itemId;
public Item item;
public BasePlayer looter;
ItemContainer loot;
public BaseEntity entityOwner;
public ulong skinId;
void Awake() {
isCreating=false;
isBlocked=false;
isCleaning=false;
isEmptied=false;
loot=GetComponent<StorageContainer>().inventory;
entityOwner=loot.entityOwner;
}
public void PlayerStoppedLooting(BasePlayer player) {
skinBox.activeBoxUsers.Remove(player.userID);
if (!isEmptied && item !=null) {
isEmptied=true;
player.GiveItem(item);
}
if (!GetComponent<BaseEntity>().IsDestroyed) GetComponent<BaseEntity>().Kill(BaseNetworkable.DestroyMode.None);
if (skinBox.enableCooldown) skinBox.cooldownTimes[player.UserIDString+"-box"]=DateTime.UtcNow;
}
void OnDestroy() {
skinBox.activeBoxUsers.Remove(looter.userID);
if (!isEmptied && item!=null) {
isEmptied=true;
looter.GiveItem(item);
}
looter.EndLooting();
}
}
Boolean CheckDistance(BasePlayer player) {
var playerPos=player.transform.position;
foreach (var id in activeBoxUsers) {
if (Vector3.Distance(playerPos, id.Value) < boxUseDistance) {
player.ChatMessage(lang.GetMessage("ToNearPlayer", this, player.UserIDString));
return false;
}
}
return true;
}
void OpenSkinBox(BasePlayer player) {
var skinBox=GameManager.server.CreateEntity("assets/prefabs/deployable/large wood storage/box.wooden.large.prefab", player.transform.position - new Vector3(UnityEngine.Random.Range(-5f, 5f), 200, UnityEngine.Random.Range(-5f, 5f)));
skinBox.UpdateNetworkGroup();
skinBox.SendNetworkUpdateImmediate();
skinBox.globalBroadcast=true;
(skinBox as BaseNetworkable).limitNetworking=true;
skinBox.Spawn();
skinBox.gameObject.AddComponent<SkinBoxHandler>().looter=player;
var container=skinBox.GetComponent<StorageContainer>();
container.inventory.capacity=1;
container.SetFlag(BaseEntity.Flags.Open, true, false);
if (QuickSort && hideQuickSort) StartLootingEntity(player.inventory.loot, container);
else player.inventory.loot.StartLootingEntity(container, false);
player.inventory.loot.AddContainer(container.inventory);
player.inventory.loot.SendImmediate();
player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", container.panelName);
container.DecayTouch();
container.SendNetworkUpdate();
activeBoxUsers[player.userID]=player.transform.position;
}
public void StartLootingEntity(PlayerLoot loot, BaseEntity targetEntity) {
FieldInfo _PositionChecks=typeof(PlayerLoot).GetField("PositionChecks", (BindingFlags.Instance | BindingFlags.NonPublic));
MethodInfo _MarkDirty=typeof(PlayerLoot).GetMethod("MarkDirty", BindingFlags.NonPublic | BindingFlags.Instance);
loot.Clear();
if (!targetEntity) return;
_PositionChecks.SetValue(loot, false);
loot.entitySource=targetEntity;
loot.itemSource=null;
_MarkDirty.Invoke(loot, null);
}
void ClearContainer(ItemContainer container) {
while (container.itemList.Count > 0) {
var item=container.itemList[0];
item.RemoveFromContainer();
item.Remove(0f);
}
}
object CanAcceptItem(ItemContainer container, Item item) {
if (container==null || item==null || container.entityOwner==null || container.entityOwner.GetComponent<SkinBoxHandler>()==null || container.entityOwner !=container.entityOwner.GetComponent<SkinBoxHandler>().entityOwner || container.entityOwner.GetComponent<SkinBoxHandler>().isCreating) return null;
if (container.entityOwner.GetComponent<SkinBoxHandler>().isBlocked || (enableCustomPerms && !CheckItemPerms(container.entityOwner.GetComponent<SkinBoxHandler>().looter, item)) || item.amount > 1 || item.isBroken || !skinsCache.ContainsKey(item.info.shortname) || (skinsCache[item.info.shortname] as List <ulong>).Count <=1 || !CheckSkinBalance(container.entityOwner.GetComponent<SkinBoxHandler>().looter, item)) return ItemContainer.CanAcceptResult.CannotAccept;
return null;
}
bool CheckItemPerms(BasePlayer player, Item item) {
string category=item.info.category.ToString();
switch (category) {
case "Weapon" : if (permission.UserHasPermission(player.UserIDString, permCustomWeapon)) return true;
break;
case "Tool" : if (permission.UserHasPermission(player.UserIDString, permCustomWeapon)) return true;
break;
case "Attire" : if (permission.UserHasPermission(player.UserIDString, permCustomPlayerwearable)) return true;
break;
case "Items" : if (permission.UserHasPermission(player.UserIDString, permCustomDeployable)) return true;
break;
case "Construction" : if (permission.UserHasPermission(player.UserIDString, permCustomDeployable)) return true;
break;
default : return true;
}
return false;
}
void OnItemAddedToContainer(ItemContainer container, Item item) {
if (container==null || item==null || container.entityOwner==null || container.entityOwner.GetComponent<SkinBoxHandler>()==null || container.entityOwner !=container.entityOwner.GetComponent<SkinBoxHandler>().entityOwner || container.entityOwner.GetComponent<SkinBoxHandler>().isCreating) return;
var lootHandler=container.entityOwner.GetComponent<SkinBoxHandler>();
lootHandler.isCreating=true;
lootHandler.itemId=item.info.itemid;
lootHandler.isEmptied=false;
string shortname=item.info.shortname;
bool hasCondition=item.hasCondition;
float condition=item.condition;
float maxCondition=item.maxCondition;
bool isWeapon=item.GetHeldEntity() !=null && item.GetHeldEntity() is BaseProjectile;
bool hasMods=false;
int contents=0;
int capacity=0;
ItemDefinition ammoType=null;
Dictionary <int, float>itemMods=new Dictionary <int, float>();
List <ulong>itemSkins=skinsCache[shortname] as List <ulong>;
if (isWeapon) {
contents=(item.GetHeldEntity() as BaseProjectile).primaryMagazine.contents;
capacity=(item.GetHeldEntity() as BaseProjectile).primaryMagazine.capacity;
ammoType=(item.GetHeldEntity() as BaseProjectile).primaryMagazine.ammoType;
if (item.contents !=null && item.contents.itemList.Count > 0) {
hasMods=true;
foreach (var mod in item.contents.itemList) itemMods.Add(mod.info.itemid, mod.condition);
}
}
item.RemoveFromContainer();
lootHandler.skinId=item.skin;
lootHandler.item=item;
container.capacity=itemSkins.Count();
var itemDef=ItemManager.FindItemDefinition(shortname);
foreach (var skin in itemSkins) {
if (container.itemList.Count==30) break;
if (excludedSkins.Contains(skin) && lootHandler.looter.net.connection.authLevel < exludedSkinsAuthLevel) continue;
Item newItem=ItemManager.Create(itemDef, 1, skin);
if (hasCondition) {
newItem.condition=condition;
newItem.maxCondition=maxCondition;
}
if (isWeapon) {
(newItem.GetHeldEntity() as BaseProjectile).primaryMagazine.contents=contents;
(newItem.GetHeldEntity() as BaseProjectile).primaryMagazine.capacity=capacity;
(newItem.GetHeldEntity() as BaseProjectile).primaryMagazine.ammoType=ammoType;
if (hasMods) {
foreach (var mod in itemMods) {
Item newMod=ItemManager.CreateByItemID((int)mod.Key, 1);
newMod.condition=Convert.ToSingle(mod.Value);
newMod.MoveToContainer(newItem.contents, -1, false);
}
newItem.contents.SetFlag(ItemContainer.Flag.IsLocked, true);
newItem.contents.SetFlag(ItemContainer.Flag.NoItemInput, true);
}
}
newItem.MoveToContainer(container, -1, false);
}
lootHandler.isCreating=false;
lootHandler.isBlocked=true;
}
void OnItemRemovedFromContainer(ItemContainer container, Item item) {
if (container==null || item==null || container.entityOwner==null || container.entityOwner.GetComponent<SkinBoxHandler>()==null || container.entityOwner !=container.entityOwner.GetComponent<SkinBoxHandler>().entityOwner || container.entityOwner.GetComponent<SkinBoxHandler>().isCreating || container.entityOwner.GetComponent<SkinBoxHandler>().isCleaning) return;
var loothandler=container.entityOwner.GetComponent<SkinBoxHandler>();
if (item.GetHeldEntity() !=null && item.GetHeldEntity() is BaseProjectile) {
if (item.contents !=null) {
item.contents.SetF
// ... [Code preview truncated due to length. Full source in attached file] ...