Skip to content
RustPluginGenerator

OnPlayerAddModifiers

OnPlayerAddModifiers is called when a player consumes an item that grants a player modifier, right before the game adds that modifier to the player.

Signature
object OnPlayerAddModifiers(BasePlayer player, Item item, ItemModConsumable consumable)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe player consuming the item. Always set when the hook fires from ItemModConsume.DoAction.
itemItemThe item being consumed; use item.info.shortname, item.amount or item.skin to tell food, tea and other consumables apart.
consumableItemModConsumableThe ItemModConsumable component of the item, which holds the modifier(s) the game is about to apply (e.g. Max Health, Radiation).

Return value

Returning null lets the game proceed and add the modifier to the player. Returning any non-null value stops the original code path, so the modifier is not applied at all; the value itself is discarded because the patched method returns void.

How it works

The hook fires inside ItemModConsume.DoAction, after the item's eat/drink effects and metabolism changes have been applied, and immediately before player.modifiers.Add(consumable.modifiers) runs. It only fires when the consumed item actually has an ItemModConsumable with modifiers and the player has a modifiers component, so plain food that only restores calories or hydration usually does not reach it.\n\nplayer is the consumer, item is the stack entry that was consumed, and consumable is the component holding the modifier values. Use it to veto or replace modifier gains: block a tea entirely, restrict which players may receive a bonus, or read the modifier values to log or price consumption.\n\nYou can narrow the hook to a specific item type by declaring the second parameter as a more specific type, for example Item is the base type, so declare exactly Item unless you know a subclass flows in; Oxide skips the call when an argument is not an instance of your parameter type. The hook runs on the server thread as part of the consume action, so keep it fast.

Example plugin: Block tea modifiers while combat-blocked

NoTeaInCombat.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("No Tea In Combat", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks tea and modifier consumables while the player was recently damaged by another player")]
    public class NoTeaInCombat : RustPlugin
    {
        private const string PermBypass = "noteaincombat.bypass";

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> lastDamage = new Dictionary<ulong, DateTime>();
        private Timer sweepTimer;

        private class PluginConfig
        {
            [JsonProperty("Combat window in seconds")]
            public double CombatWindow = 15.0;

            [JsonProperty("Refund blocked items")]
            public bool Refund = true;
        }

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Blocked"] = "You cannot use {0} while in combat.",
                ["Refunded"] = "The {0} has been returned to your inventory."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);

        private void Init()
        {
            permission.RegisterPermission(PermBypass, this);
            sweepTimer = timer.Every(30f, Sweep);
        }

        private void Unload()
        {
            sweepTimer?.Destroy();
            lastDamage.Clear();
        }

        private void Sweep()
        {
            DateTime cutoff = DateTime.UtcNow.AddSeconds(-Math.Max(config.CombatWindow, 1.0) * 4);
            var stale = new List<ulong>();
            foreach (var entry in lastDamage)
            {
                if (entry.Value < cutoff) stale.Add(entry.Key);
            }
            foreach (ulong id in stale) lastDamage.Remove(id);
        }

        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (info == null) return;
            var victim = entity as BasePlayer;
            if (victim == null || victim.IsNpc) return;
            if (info.InitiatorPlayer == null || info.InitiatorPlayer == victim) return;
            lastDamage[victim.userID.Get()] = DateTime.UtcNow;
        }

        private object OnPlayerAddModifiers(BasePlayer player, Item item, ItemModConsumable consumable)
        {
            if (player == null || item == null || item.info == null || consumable == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            DateTime stamp;
            if (!lastDamage.TryGetValue(player.userID.Get(), out stamp)) return null;
            if ((DateTime.UtcNow - stamp).TotalSeconds > config.CombatWindow) return null;

            string shortname = item.info.shortname;
            if (shortname == null || !shortname.StartsWith("tea")) return null;

            string displayName = item.info.displayName != null ? item.info.displayName.english : shortname;
            player.ChatMessage(Msg("Blocked", player.UserIDString, displayName));

            if (config.Refund)
            {
                Item replacement = ItemManager.CreateByItemID(item.info.itemid, 1, item.skin);
                if (replacement != null && player.inventory.GiveItem(replacement))
                {
                    player.ChatMessage(Msg("Refunded", player.UserIDString, displayName));
                }
            }

            return true;
        }
    }
}

The plugin records a timestamp for each player whenever they take damage from another player, using OnEntityTakeDamage. When OnPlayerAddModifiers fires, it checks whether the consumed item's shortname starts with tea and whether the player took player damage within the configured window. If both are true, it returns a non-null value to suppress the modifier, notifies the player through a localized message and refunds the tea as a new item. A timer.Every sweep clears stale timestamps and is destroyed in Unload; all player-facing text goes through the language file.

Common use cases

  • Combat restrictions

    Deny tea and other modifier consumables while a player is in combat, and give the item back so consumption is not punished.

  • Permission-based bonuses

    Return a value to block modifiers for players without a VIP permission, or gate specific consumables behind a permission.

  • Audit/logging

    Record which modifier items players consume and how often, using item.info.shortname and the consumable's modifier list, without blocking anything.

Pitfalls

  • Only modifiers are affected

    The hook fires after metabolism effects are applied. Blocking it stops the modifier gain but not the hydration, calories or the consumed item removal.

  • Item is already consumed

    By the time the hook runs the stack has been reduced. If you want the player to keep the item, create a fresh one with ItemManager.CreateByName and GiveItem it back.

  • No modifiers, no call

    Food that only restores calories or hydration never reaches this hook. Use OnPlayerMetabolize or OnItemUse for broader consume tracking.

Build a plugin with OnPlayerAddModifiers

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Combat restrictions using the OnPlayerAddModifiers hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with OnPlayerAddModifiers

Generate