C#:
using System;
using System.Collections.Generic;
using HarmonyLib;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
[Info("Container Rules", "Beast_", "1.0.0")]
[Description("Modifies container acceptance rules for players with permissions.")]
public class ContainerRules : RustPlugin
{
#region Fields
private static ContainerRules instance;
private Configuration config;
private Harmony harmony;
private const string PermUse = "containerrules.use";
#endregion
#region Configuration
private class Configuration
{
[JsonProperty("Global Settings")]
public GlobalSettings Global { get; set; } = new GlobalSettings();
[JsonProperty("Chat Settings")]
public ChatSettings chatSettings { get; set; } = new ChatSettings();
[JsonProperty("Container Rules", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, ContainerRule> Rules { get; set; } = new Dictionary<string, ContainerRule>()
{
["cupboard.tool.deployed"] = new ContainerRule
{
Enabled = true,
AllowedCategories = new List<string> { "Resources" },
BlockedCategories = new List<string>(),
AllowedShortnames = new List<string> (),
BlockedShortnames = new List<string> (),
RequiresPermission = true
},
["cupboard.tool.retro.deployed"] = new ContainerRule
{
Enabled = true,
AllowedCategories = new List<string> { "Resources" },
BlockedCategories = new List<string>(),
AllowedShortnames = new List<string>(),
BlockedShortnames = new List<string>(),
RequiresPermission = true
},
["cupboard.tool.shockbyte.deployed"] = new ContainerRule
{
Enabled = true,
AllowedCategories = new List<string> { "Resources" },
BlockedCategories = new List<string>(),
AllowedShortnames = new List<string>(),
BlockedShortnames = new List<string>(),
RequiresPermission = true
},
["fridge.deployed"] = new ContainerRule
{
Enabled = true,
AllowedCategories = new List<string> { "Food" },
BlockedCategories = new List<string>(),
AllowedShortnames = new List<string>(),
BlockedShortnames = new List<string>(),
RequiresPermission = true
}
};
public class GlobalSettings
{
[JsonProperty("Notify Player when container rules are applied")]
public bool NotifyPlayer { get; set; } = true;
[JsonProperty("Use Tip Notifications instead of Chat Messages")]
public bool UseTipNotifications { get; set; } = true;
}
public class ChatSettings
{
[JsonProperty("Chat Prefix")]
public string Prefix = "<color=#00EDED>[ContainerRules]</color>: ";
[JsonProperty("Icon SteamID")]
public ulong IconSteamId = 0;
}
public class ContainerRule
{
[JsonProperty("Enabled")]
public bool Enabled { get; set; }
[JsonProperty("Allowed Categories (Names - Empty = Accept All)")]
public List<string> AllowedCategories { get; set; }
[JsonProperty("Blocked Categories (Names)")]
public List<string> BlockedCategories { get; set; }
[JsonProperty("Allowed Shortnames (Empty = Accept All)")]
public List<string> AllowedShortnames { get; set; }
[JsonProperty("Blocked Shortnames")]
public List<string> BlockedShortnames { get; set; }
[JsonProperty("Requires Permission")]
public bool RequiresPermission { get; set; }
}
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
protected override void LoadDefaultConfig() => config = new Configuration();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
if (config == null) throw new JsonException();
}
catch
{
PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config, true);
#endregion
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["ItemBlocked_Shortname"] = "You cannot store {0} in this container (blocked item).",
["ItemBlocked_Category"] = "You cannot store {0} in this container (blocked category: {1}).",
["ItemNotAllowed_Category"] = "You cannot store {0} in this container (only {1} allowed).",
["ItemNotAllowed_Shortname"] = "You cannot store {0} in this container (item not whitelisted).",
["ItemNotAllowed_Both"] = "You cannot store {0} in this container (doesn't match container restrictions).",
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
{
var message = lang.GetMessage(key, this, userId);
return args.Length > 0 ? string.Format(message, args) : message;
}
private void Reply(BasePlayer player, string key, params object[] args)
{
if (player == null || !config.Global.NotifyPlayer)
return;
if (config.Global.UseTipNotifications)
player.ShowToast(GameTip.Styles.Red_Normal, Msg(key, player.UserIDString, args));
else
Player.Message(player, Msg(key, player.UserIDString, args), config.chatSettings.Prefix, config.chatSettings.IconSteamId);
}
#endregion
#region Hooks
private void Init()
{
instance = this;
permission.RegisterPermission(PermUse, this);
harmony = new Harmony(Name + "Patch");
harmony.PatchAll();
}
private void Unload()
{
harmony?.UnpatchAll(harmony.Id);
instance = null;
}
#endregion
#region Harmony
[HarmonyPatch(typeof(ItemContainer), nameof(ItemContainer.CanAcceptItem))]
public static class CanAcceptItem_Patch
{
[HarmonyPrefix]
public static bool Prefix(ItemContainer __instance, Item item, int targetPos, ref ItemContainer.CanAcceptResult __result)
{
if (__instance == null || item == null || __instance.entityOwner == null || instance == null)
return true;
string prefabName = __instance.entityOwner.ShortPrefabName;
if (instance.config.Rules.TryGetValue(prefabName, out var rule))
{
if (!rule.Enabled) return true;
string category = item.info.category.ToString();
string shortname = item.info.shortname;
string displayName = item.info.displayName.english;
var looter = instance.GetLooter(__instance);
if (rule.RequiresPermission)
{
if (looter == null || !instance.permission.UserHasPermission(looter.UserIDString, PermUse))
return true;
}
if (rule.BlockedShortnames.Contains(shortname))
{
__result = ItemContainer.CanAcceptResult.CannotAccept;
if (looter != null)
instance.Reply(looter, "ItemBlocked_Shortname", displayName);
return false;
}
if (rule.BlockedCategories.Contains(category))
{
__result = ItemContainer.CanAcceptResult.CannotAccept;
if (looter != null)
instance.Reply(looter, "ItemBlocked_Category", displayName, category);
return false;
}
bool hasAllowedCategories = rule.AllowedCategories.Count > 0;
bool hasAllowedShortnames = rule.AllowedShortnames.Count > 0;
if (!hasAllowedCategories && !hasAllowedShortnames)
{
__result = ItemContainer.CanAcceptResult.CanAccept;
return false;
}
bool categoryMatches = !hasAllowedCategories || rule.AllowedCategories.Contains(category);
bool shortnameMatches = !hasAllowedShortnames || rule.AllowedShortnames.Contains(shortname);
if (categoryMatches && shortnameMatches)
{
__result = ItemContainer.CanAcceptResult.CanAccept;
return false;
}
else
{
__result = ItemContainer.CanAcceptResult.CannotAccept;
if (looter != null)
{
if (hasAllowedCategories && hasAllowedShortnames)
instance.Reply(looter, "ItemNotAllowed_Both", displayName);
else if (hasAllowedCategories)
instance.Reply(looter, "ItemNotAllowed_Category", displayName, string.Join(", ", rule.AllowedCategories));
else
instance.Reply(looter, "ItemNotAllowed_Shortname", displayName);
}
return false;
}
}
return true;
}
}
#endregion
#region Helpers
private BasePlayer GetLooter(ItemContainer targetContainer)
{
if (targetContainer == null)
return null;
if (targetContainer.playerOwner != null)
return targetContainer.playerOwner;
var entityOwner = targetContainer.entityOwner;
if (entityOwner == null)
return null;
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
var inventory = player.inventory;
if (inventory?.loot == null)
continue;
if (player.inventory.loot.entitySource == entityOwner)
return player;
}
return null;
}
#endregion
}
}