Skip to content
RustPluginGenerator

OnMagazineReload

OnMagazineReload is called at the start of BaseProjectile.TryReloadMagazine, before the weapon moves rounds from an ammo container into its magazine. Returning a bool from the hook replaces the method's result and skips the normal reload path.

Signature
object OnMagazineReload(BaseProjectile projectile, IAmmoContainer ammoSource, BasePlayer ownerPlayer)

Parameters

Hook parameters
ParameterTypeDescription
projectileBaseProjectileThe BaseProjectile instance performing the reload. Use it to read the weapon's primary and attached magazines, ammo types and owner chain.
ammoSourceIAmmoContainerThe IAmmoContainer the weapon is reloading from (the inventory container that supplies rounds). Useful to count or remove ammo before the reload succeeds.
ownerPlayerBasePlayerThe BasePlayer holding the weapon, resolved via GetOwnerPlayer(). It is null for weapons without a player owner, such as turrets or NPC-held projectiles.

Return value

Return null (or any non-bool value) to let the normal reload proceed. Return a bool to make that value the result of TryReloadMagazine: true reports a successful reload without the game moving any ammo, false reports failure even though nothing was attempted.

How it works

The hook fires inside BaseProjectile.TryReloadMagazine, which the game calls when a player (or an automated weapon such as a turret) asks a projectile weapon to fill its magazine from a container. Because it runs before the internal TryReload call, it is the earliest point where a plugin can veto or short-circuit a reload.

projectile is the weapon requesting the reload; use it to read primaryMagazine/attachedMagazine, ammo definitions, OwnerID, or GetParentEntity(). ammoSource is the container the rounds would be pulled from, so it is the place to check available stack amounts. ownerPlayer is resolved through GetOwnerPlayer() and can be null, so always null-check it before touching player inventory or permissions.

Declaring projectile as a more specific type (for example BaseProjectile is already specific; narrowing further is not practical) does not help here since every call passes a BaseProjectile. You can declare ownerPlayer as BasePlayer (default) or a more derived type only if you know all callers match it; otherwise Oxide skips calls. Keep the body cheap: this runs on every attempted reload, including failed ones.

Example plugin: Block magazine reloads when no ammo is available

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

namespace Oxide.Plugins
{
    [Info("Magazine Reload Guard", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks magazine reloads for weapons whose ammo requires a permission.")]
    public class MagazineReloadGuard : RustPlugin
    {
        private const string PermBypass = "magazinereloadguard.bypass";

        private PluginConfig config;

        public class PluginConfig
        {
            [JsonProperty("Show a chat warning when a reload is blocked")]
            public bool WarnOnBlock { get; set; } = true;

            [JsonProperty("Only enforce on listed ammo shortnames (empty = all)")]
            public List<string> RestrictedAmmo { get; set; } = new List<string>();
        }

        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 are not permitted to reload {0}."
            }, 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 object OnMagazineReload(BaseProjectile projectile, IAmmoContainer ammoSource, BasePlayer ownerPlayer)
        {
            if (projectile == null || ammoSource == null || ownerPlayer == null)
                return null;

            if (!ownerPlayer.IsConnected || ownerPlayer.IsAdmin)
                return null;

            if (permission.UserHasPermission(ownerPlayer.UserIDString, PermBypass))
                return null;

            var magazine = projectile.primaryMagazine;
            if (magazine == null || magazine.ammoType == null)
                return null;

            string shortname = magazine.ammoType.shortname;
            if (config.RestrictedAmmo != null && config.RestrictedAmmo.Count > 0
                && !config.RestrictedAmmo.Contains(shortname))
                return null;

            if (config.WarnOnBlock)
                ownerPlayer.ChatMessage(Msg("Blocked", ownerPlayer.UserIDString, magazine.ammoType.displayName.english));

            // Report the reload as handled so vanilla skips moving ammo.
            return true;
        }
    }
}

The plugin watches every attempt to reload a projectile weapon. When the hook fires it verifies ownerPlayer is connected and fetches the weapon's primary magazine ammo item id. If that ammo needs a permission (magazinereadguard.bypass) and the player lacks it, a warning is printed and the hook returns true, which reports the reload as handled so the game does not touch the ammo container. Otherwise it returns null and the vanilla reload runs unchanged. The permission is registered in Init and messages go through the language file so servers can localize them.

Common use cases

  • Ammo economy gating

    Block or allow reloads based on a custom ammo currency system by inspecting ammoSource contents and returning a bool to short-circuit.

  • Weapon restrictions

    Prevent specific players (by team, permission or zone) from reloading certain weapons by returning true and notifying them through chat.

  • Reload analytics

    Count reload attempts per weapon or per player to feed a stats dashboard, returning null so gameplay stays untouched.

Pitfalls

  • ownerPlayer can be null

    Turrets, traps and NPC weapons call TryReloadMagazine with no player owner. Null-check ownerPlayer before reading permissions or inventory.

  • Returning true skips ammo movement

    A true result tells the game the reload succeeded, so nothing is consumed from ammoSource. Only return true when your plugin has handled the ammo logic itself.

  • Fires on failed reloads too

    This hook runs before the ammo check, so it is called even when no rounds are available. Avoid expensive per-call work such as scanning the whole inventory.

Build a plugin with OnMagazineReload

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

Prompt: Ammo economy gating using the OnMagazineReload 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 OnMagazineReload

Generate