Skip to content
RustPluginGenerator

OnAmmoUnload

OnAmmoUnload is called from BaseProjectile.UnloadAmmo before a weapon's magazine is emptied back into the player's inventory. Returning a non-null value cancels the unload.

Signature
object OnAmmoUnload(BaseProjectile component, Item item, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
componentBaseProjectileThe BaseProjectile of the held weapon whose magazine is being unloaded. Use it to read primaryMagazine, weapon stats, or the owner entity.
itemItemThe weapon Item being unloaded. Gives you item.info (shortname, itemid), amount, skin, condition, and the item container it lives in.
playerBasePlayerThe BasePlayer who triggered the unload. Can be null in edge cases, so check it before calling player methods such as ChatMessage or IsAdmin.

Return value

Return null to let the game continue unloading the magazine normally. Return any non-null value (for example true) to stop UnloadAmmo from proceeding, leaving the rounds in the magazine; the game returns from the method immediately.

How it works

The hook fires inside BaseProjectile.UnloadAmmo(Item item, BasePlayer player) right after the game resolves the weapon's BaseProjectile component and confirms canUnloadAmmo. It runs before the magazine contents are read and moved back to the player, so it sees the weapon in its still-loaded state. This is the action players trigger by pressing the unload key (usually R by default) while holding a weapon with rounds in the magazine — it is not the reload path.

Use component to inspect primaryMagazine.contents, primaryMagazine.ammoType, or whether the weapon is attached to a deployable. item gives you the shortname, skin, and condition if you want to filter by weapon type or cosmetic. player is the actor, useful for permission checks, chat warnings, or logging, but treat it as nullable.

To narrow the hook to specific weapons, declare the parameter as a more specific type only when every such weapon shares that type (e.g. BaseProjectile covers all guns, so narrowing further usually skips too much). Returning non-null from any plugin cancels the unload.

Example plugin: Block ammo unloading during combat

NoCombatUnload.cs
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("No Combat Unload", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks players from unloading weapon magazines while they are in combat.")]
    public class NoCombatUnload : RustPlugin
    {
        private const string PermBypass = "nocombatunload.bypass";

        private PluginConfig config;
        private readonly Dictionary<string, float> lastCombat = new Dictionary<string, float>();

        private class PluginConfig
        {
            [JsonProperty("Enable combat block")]
            public bool Enabled { get; set; } = true;

            [JsonProperty("Combat cooldown seconds")]
            public float CooldownSeconds { get; set; } = 8f;
        }

        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>
            {
                ["UnloadBlocked"] = "You cannot unload ammo while in combat. Wait a few seconds and try again."
            }, 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);
        }

        private void MarkCombat(BasePlayer player)
        {
            if (player == null || player.IsNpc) return;
            lastCombat[player.UserIDString] = UnityEngine.Time.realtimeSinceStartup;
        }

        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity is BasePlayer player) MarkCombat(player);
        }

        private void OnWeaponFired(BaseProjectile projectile, BasePlayer player, ItemModProjectile mod, ProtoBuf.ProjectileShoot projectiles)
        {
            MarkCombat(player);
        }

        private object OnAmmoUnload(BaseProjectile component, Item item, BasePlayer player)
        {
            if (!config.Enabled || player == null || item?.info == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            float last;
            if (!lastCombat.TryGetValue(player.UserIDString, out last)) return null;
            if (UnityEngine.Time.realtimeSinceStartup - last < config.CooldownSeconds)
            {
                player.ChatMessage(Msg("UnloadBlocked", player.UserIDString));
                return true;
            }
            return null;
        }

        private void Unload()
        {
            lastCombat.Clear();
        }
    }
}

The plugin refuses unload requests while the player is in combat, defined as taking damage or firing within a configurable number of seconds. OnEntityTakeDamage timestamps any player who takes damage, and OnWeaponFired timestamps players who shoot; both write into a dictionary keyed by UserIDString. OnAmmoUnload checks that timestamp against Time.realtimeSinceStartup; if the cooldown is still active the hook returns true to cancel the unload and sends a chat message through language strings so the player knows why. Config exposes the cooldown seconds and whether the block is enabled. Timestamps are cleared on unload and entries are read defensively, so a null player or missing entry never throws.

Common use cases

  • Prevent unloading mid-fight

    Reject unload requests while a player is in active combat to stop magazine-dumping tricks during firefights.

  • Restrict unload to specific weapons

    Allow normal unloading but cancel for listed weapon shortnames (for example an event-only rifle) by checking item.info.shortname.

  • Log unload events for admins

    Record who unloaded which weapon with what magazine contents before letting the game proceed, useful for economy or exploit auditing.

Pitfalls

  • This is not the reload hook

    OnAmmoUnload only fires when a magazine is emptied back to the player. Reload behavior lives in OnMagazineReload and OnWeaponReload, so blocking unload doesn't block reloading.

  • Player can be null

    The player argument is not guaranteed non-null; check it before calling chat, permission, or teleport methods.

  • Returning true blocks silently

    A non-null return cancels the unload with no built-in feedback. Send your own message so players don't think the key is broken.

Build a plugin with OnAmmoUnload

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

Prompt: Prevent unloading mid-fight using the OnAmmoUnload 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 OnAmmoUnload

Generate