OnAmmoSwitch
OnAmmoSwitch is called when a player loads a new ammo type into a weapon's magazine, right before the game returns any existing rounds and swaps the ammo type. Returning a non-null value cancels the switch.
object OnAmmoSwitch(BaseProjectile projectile, BasePlayer ownerPlayer, ItemDefinition itemDefinition)Parameters
| Parameter | Type | Description |
|---|---|---|
projectile | BaseProjectile | The BaseProjectile whose magazine is being switched to a different ammo type. Useful for reading primaryMagazine.contents, ammoType and the weapon's current state. |
ownerPlayer | BasePlayer | The player who owns the weapon and triggered the switch. Can be null for projectiles not held by a player, so null-check it before using inventory or permission APIs. |
itemDefinition | ItemDefinition | The ammo ItemDefinition being loaded into the magazine (already validated to be a valid ammo type for the weapon). Read shortname, itemid and displayName.english for messages and rules. |
Return value
Return null to let the switch proceed normally. Return any non-null value (for example true, or a formatted string in some Oxide versions) to stop the original code path, which cancels the ammo switch: existing rounds stay in the magazine and the new ammo type is not applied.
How it works
The hook fires inside BaseProjectile.SwitchAmmoTo, after the game has confirmed that itemDefinition exists and that its ItemModProjectile accepts the weapon's ammo types. It runs before the magazine is touched, so primaryMagazine.contents still holds the old round count and primaryMagazine.ammoType still points at the previous ammo. That makes the hook the right place to inspect or block a change of ammo type.
Use projectile to identify the weapon and its magazine, ownerPlayer to check permissions, inventory or team state, and itemDefinition to decide based on the incoming ammo (shortname or itemid). Declaring ownerPlayer as BaseCombatEntity instead of BasePlayer does not help here: the game always passes the owning player, so keep the BasePlayer type and null-check it. If you only care about specific weapons, compare projectile.ShortPrefabName before doing anything expensive. Because the hook runs once per switch and not per frame, light logic is fine, but avoid blocking the switch without giving the player feedback through chat or lang messages.
Example plugin: Block ammo swaps in the magazine while a player is in a PvP combat block
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Ammo Switch Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks selected ammo types from being loaded into weapon magazines.")]
public class AmmoSwitchGuard : RustPlugin
{
private const string PermBypass = "ammoswitchguard.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Blocked ammo shortnames")]
public List<string> BlockedShortnames = new List<string> { "ammo.rocket.hv", "ammo.rifle.explosive" };
[JsonProperty("Block swaps while in combat")]
public bool BlockInCombat = true;
[JsonProperty("Combat window in seconds")]
public float CombatWindow = 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>
{
["BlockedAmmo"] = "That ammo type cannot be loaded into this weapon.",
["InCombat"] = "You cannot change ammo type while in combat."
}, this);
}
private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private object OnAmmoSwitch(BaseProjectile projectile, BasePlayer ownerPlayer, ItemDefinition itemDefinition)
{
if (projectile == null || itemDefinition == null) return null;
if (ownerPlayer == null || !ownerPlayer.IsConnected) return null;
if (permission.UserHasPermission(ownerPlayer.UserIDString, PermBypass)) return null;
if (config.BlockedShortnames != null && config.BlockedShortnames.Contains(itemDefinition.shortname))
{
ownerPlayer.ChatMessage(Msg("BlockedAmmo", ownerPlayer.UserIDString));
return true;
}
if (config.BlockInCombat && Interface.Oxide.CallHook("IsPlayerInCombat", ownerPlayer) != null)
{
ownerPlayer.ChatMessage(Msg("InCombat", ownerPlayer.UserIDString));
return true;
}
return null;
}
}
}The plugin keeps a configurable list of blocked ammo shortnames and a combat cooldown. When OnAmmoSwitch fires, it null-checks the player and weapon, checks whether the incoming ammo is on the blocked list, and if so denies the switch and tells the player which ammo is restricted. It also blocks swaps for players who recently fired a weapon, tracked with a NextTick-free timestamp set in OnWeaponFired, so players cannot re-ammo mid-fight when BlockInCombat is enabled. Denial is done by returning true from the hook. All player text goes through the language file, and the config is reloadable with LoadDefaultConfig.
Common use cases
Restrict premium ammo on starter weapons
Return non-null when a low-tier weapon tries to load high-grade ammo, keeping early-game balance intact without touching the loot tables.
Enforce an ammo whitelist per permission group
Check
ownerPlayerin the hook and only allow the switch if the player has a permission for that ammo shortname, printing a lang message on denial.Audit ammo changes for admins
Log weapon prefab, player name and ammo shortname to a data file (throttled) so staff can review suspicious ammo switching after a raid.
Pitfalls
Returning false still cancels
The injection checks for a non-null return, not a truthy one. Returning
falsealso stops the switch, so always returnnullwhen you want the game to continue.Owner can be null
ownerPlayermay be null for projectiles not held by a player. Dereference it only after a null check or you risk an exception inside the hook.Contents still hold old ammo
The hook runs before the game refills the magazine, so
primaryMagazine.contentsdescribes the outgoing state, not the incoming one. Do not use it to judge the new ammo.
Build a plugin with OnAmmoSwitch
Describe what should happen and get a compiled plugin that uses this hook.
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 OnAmmoSwitch
Generate