C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Rust.Workshop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;
///Скачано с дискорд сервера Rust Edit [PRO+]
///discord.gg/9vyTXsJyKR
namespace Oxide.Plugins {
[Info("InstantCraft", "discord.gg/9vyTXsJyKR", "1.0.2")]
[Description("Instant craft items(includes normal speed list and blacklist)")]
internal class InstantCraft : RustPlugin {
#region Quitting
private void Unload() {
if (_getSkinsCoroutine != null) ServerMgr.Instance.StopCoroutine(_getSkinsCoroutine);
}
#endregion
#region Config setup
private class AllowedItemsConfig {
[JsonProperty("Тип списка (0 - никак не управлять предметами, 1 - чёрный список, 2 - белый список).")]
private int _type;
[JsonProperty("Список предметов")]
public List<string> Items;
[JsonIgnore]
public readonly List<ItemDefinition> ItemsDefinitions = new List<ItemDefinition>();
#region Default Config
public static AllowedItemsConfig DefaultConfig => new AllowedItemsConfig {
_type = 0, Items = new List<string> {
"explosive.satchel", "grenade.f1"
}
}
;
#endregion
public bool ShouldRandomizeSkin(ItemDefinition definition) {
ulong playerid = 4855808;
switch (_type) {
case 1 : return ItemsDefinitions.All(x => x != definition);
case 2 : return ItemsDefinitions.Any(x => x == definition);
default : return true;
}
}
}
private class SkinSettings {
[JsonProperty("Присваивать случайный скин")]
public bool RandomSkins;
[JsonProperty("Привилегия для получения случайного скина (null - все игроки получают случайные скины)")]
public string Permission;
[JsonProperty("Использовать основные скины из игры")]
public bool UseInGame;
[JsonProperty("Использовать принятые скины из мастерской")]
public bool UseAccepted;
[JsonProperty("Использовать дополнительный список скинов из мастерской")]
public bool UseCustom;
[JsonProperty("Дополнительные скины из мастерской")]
public List<ulong> AdditionalSkins;
[JsonProperty("Список предметов, которым присваивается случайный скин")]
public AllowedItemsConfig ItemList;
#region Default Config
public static SkinSettings DefaultConfig => new SkinSettings {
RandomSkins = false, UseInGame = true, UseAccepted = true, UseCustom = false, Permission = nameof(InstantCraft) + ".Random", ItemList = AllowedItemsConfig.DefaultConfig, AdditionalSkins = new List<ulong> {
2292480999
}
}
;
#endregion
[JsonIgnore]
public bool IsPermissionEmpty => string.IsNullOrEmpty(Permission) || Permission == "null";
public bool CanUse(BasePlayer player) {
return IsPermissionEmpty || player.IPlayer.HasPermission(Permission);
}
}
private class PluginConfig {
[JsonIgnore]
public readonly List<ItemDefinition> Blacklist = new List<ItemDefinition>();
[JsonIgnore]
public readonly List<ItemDefinition> NormalList = new List<ItemDefinition>();
[JsonProperty("Привилегия для мгновенного крафта (null - мгновенный крафт работает для всех)")]
public string CraftPermission;
[JsonProperty("Предметы, крафтить которые запрещено")]
public List<string> BlacklistString;
[JsonProperty("Предметы со стандартной скоростью крафта")]
public List<string> NormalListString;
[JsonProperty("Формат сообщений в чате")]
public string ChatFormat;
[JsonProperty("SteamID отправителя сообщений в чате")]
public ulong ChatMessageSteamId;
[JsonProperty("Разделять стаки при крафте")]
public bool SplitStacks;
[JsonProperty("Случайные скины при крафте (в случае, если игрок не выбрал скин)")]
public SkinSettings SkinSettings;
#region Default Config
public static PluginConfig DefaultConfig => new PluginConfig {
CraftPermission = "null", ChatFormat = "<color=#42d4f4>[InstantCraft]</color>: {0}", ChatMessageSteamId = 0, NormalListString = new List<string> {
"rock", ItemNamePlaceholder
}
, BlacklistString = new List<string> {
ItemNamePlaceholder
}
, SplitStacks = true, SkinSettings = SkinSettings.DefaultConfig
}
;
#endregion
public static void InitList(List<string> stringList, List<ItemDefinition> definitions, List<string> wrongNames) {
stringList.ForEach(x => {
if (x == ItemNamePlaceholder) return;
var def = ItemManager.FindItemDefinition(x);
if (def == null) {
wrongNames.Add(x);
return;
}
definitions.Add(def);
}
);
}
[JsonIgnore]
public bool IsCraftPermissionEmpty => string.IsNullOrEmpty(CraftPermission) || CraftPermission == "null";
public bool CanInstantCraft(BasePlayer player) {
return IsCraftPermissionEmpty || player.IPlayer.HasPermission(CraftPermission);
}
}
#endregion
#region Initializing
private void OnServerInitialized() {
PrintWarning("\n-----------------------------\n" + " Author - https://topplugin.ru/\n" + " VK - https://vk.com/rustnastroika\n" + " Discord - https://discord.com/invite/5DPTsRmd3G\n" + "-----------------------------");
if (!_config.SkinSettings.UseCustom) return;
if (_config.SkinSettings.AdditionalSkins.Count == 0) {
PrintWarning("Custom skins turned on, but no skins specified in the list.");
return;
}
_getSkinsCoroutine = ServerMgr.Instance.StartCoroutine(GetSkins());
}
private IEnumerator GetSkins() {
var added = 0;
var failed = Pool.GetList<string>();
yield return GetSkins(_config.SkinSettings.AdditionalSkins.Where(x => !_data.ResolvedSkins.ContainsKey(x)), (x, y) => {
_data.ResolvedSkins[y] = x;
}
, x => failed.Add(x + " [FAILED TO RESOLVE]"));
foreach (var resolvedSkin in _data.ResolvedSkins) {
if (TryAddCustomSkin(resolvedSkin.Value, new SkinInfo(resolvedSkin.Key))) added++;
else failed.Add($"{resolvedSkin.Key} [{resolvedSkin.Value}]");
}
Puts("Initialized {0} custom skins from the workshop.", added);
if(failed.Count>0) Puts("Failed to add workshop items: {0}", string.Join(", ", failed));
Pool.FreeList(ref failed);
SaveData();
}
#region Workhop Skin Resolver
private IEnumerator GetSkins(IEnumerable<ulong> workshopIds, Action<string, ulong> onSuccess, Action<ulong> onFail) {
foreach (var request in BuildRequests(workshopIds)) {
using (var www = UnityWebRequest.Post(GetPublishedAPI, request)) {
yield return www.SendWebRequest();
if (!www.isDone) {
PrintError("Unable to complete Steam API request!");
break;
}
if (www.isHttpError || www.isNetworkError) {
PrintError("Steam API request resulted in error: {0}", www.error);
break;
}
var text = www.downloadHandler.text;
if (string.IsNullOrEmpty(text)) {
PrintError("Steam API return empty response!");
break;
}
var json = JsonConvert.DeserializeObject<SteamApiResponse>(text, _errorHandler);
if(json?.response == null || json.response.resultcount == 0) {
PrintError("Steam API return empty response!");
break;
}
foreach (var field in json.response.publishedfiledetails) {
if (!field.IsValid) continue;
ulong id;
if (!field.TryGetId(out id)) continue;
var success = false;
foreach (var fieldTag in field.tags) {
if (fieldTag.tag.StartsWith("Skin") || fieldTag.tag.StartsWith("Version")) continue;
var skinnable = Skinnable.FindForItem(fieldTag.tag);
if (string.IsNullOrEmpty(skinnable?.ItemName)) continue;
onSuccess?.Invoke(skinnable.ItemName, id);
success = true;
break;
}
if(!success) onFail?.Invoke(id);
}
}
}
}
#region Steam Api Response
public class Tag {
public string tag {
get;
set;
}
}
public class Publishedfiledetail {
public string publishedfileid {
get;
set;
}
public int result {
get;
set;
}
public string creator {
get;
set;
}
public int creator_app_id {
get;
set;
}
public int consumer_app_id {
get;
set;
}
public string filename {
get;
set;
}
public int file_size {
get;
set;
}
public string preview_url {
get;
set;
}
public string hcontent_preview {
get;
set;
}
public string title {
get;
set;
}
public string description {
get;
set;
}
public int time_created {
get;
set;
}
public int time_updated {
get;
set;
}
public int visibility {
get;
set;
}
public int banned {
get;
set;
}
public string ban_reason {
get;
set;
}
public int subscriptions {
get;
set;
}
public int favorited {
get;
set;
}
public int lifetime_subscriptions {
get;
set;
}
public int lifetime_favorited {
get;
set;
}
public int views {
get;
set;
}
public List<Tag> tags {
get;
set;
}
[JsonIgnore]
public bool IsValid => !string.IsNullOrEmpty(preview_url) && tags != null;
public bool TryGetId(out ulong id) => ulong.TryParse(publishedfileid, out id);
}
public class Response {
public int result {
get;
set;
}
public int resultcount {
get;
set;
}
public List<Publishedfiledetail> publishedfiledetails {
get;
set;
}
}
public class SteamApiResponse {
public Response response {
get;
set;
}
}
#endregion
private readonly JsonSerializerSettings _errorHandler = new JsonSerializerSettings {
Error = (sender, args) => args.ErrorContext.Handled = true
}
;
private IEnumerable<WWWForm> BuildRequests(IEnumerable<ulong> workshopIds) {
var skins = workshopIds.Select(x => x.ToString()).ToList();
while (skins.Count > 0) {
var amount = skins.Count < 100 ? skins.Count : 100;
WWWForm form = new WWWForm();
form.AddField("itemcount", amount.ToString());
for (var i = 0; i < amount; i++) {
form.AddField($"publishedfileids[{i}]", skins[0]);
skins.RemoveAt(0);
}
yield return form;
}
}
#endregion
#endregion
#region Vars
private PluginConfig _config;
private PluginData _data;
private const string ItemNamePlaceholder = "shortname или полное имя предмета на английском";
private readonly Dictionary<string, HashSet<SkinInfo>> _customSkins = new Dictionary<string, HashSet<SkinInfo>>();
private Coroutine _getSkinsCoroutine;
private const string GetPublishedAPI = "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/";
#endregion
#region Data
private class PluginData {
public Dictionary<ulong, string> ResolvedSkins = new Dictionary<ulong, string>();
}
#endregion
#region Config and Data Initialization
// ... [Code preview truncated due to length. Full source in attached file] ...