Skip to content
RustPluginGenerator

CanSamSiteShoot

CanSamSiteShoot is called by SamSite.WeaponTick when a sam site has a valid target and is ready to fire, before the shot is processed. Returning a non-null value stops the firing sequence for that tick.

Signature
object CanSamSiteShoot(SamSite samSite)

Parameters

Hook parameters
ParameterTypeDescription
samSiteSamSiteThe SamSite entity that is about to fire. Null-check it before use; it exposes the mounted weapon, ammo container (inventory), and the stored target list.

Return value

Returning null lets the sam site continue and fire if it has ammo. Returning any non-null value makes the hook exit the injection site's CanSamSiteShoot == null check, so the remaining fire code is skipped for that tick and no ammo is consumed.

How it works

The hook fires inside SamSite.WeaponTick, after the site has confirmed it has a target and before it consumes a low ammo item and fires. It is called on the server only, roughly once per firing interval per sam site while a valid target exists, so it can run frequently on busy servers with many sites. Use the samSite argument to inspect the entity: samSite.inventory is the ammo container (rockets), samSite.staticRespawn indicates a static site that never runs out of ammo, and the target list can be read to decide whether this shot should be allowed. The parameter type is fixed as SamSite; you cannot narrow it to a more specific subclass. Because the method runs often, avoid heavy work (raycasts, database calls, large loops) and never print messages here. Return null to allow the shot, or any non-null value (a bool, string, or empty object) to suppress firing for that tick.

Example plugin: Block unwanted sam site shots

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

namespace Oxide.Plugins
{
    [Info("Sam Site Firing Control", "RustPluginGenerator", "1.0.0")]
    [Description("Allows admins to block individual sam sites from firing without removing ammo.")]
    public class SamSiteFiringControl : RustPlugin
    {
        private const string PermAdmin = "samsitefiringcontrol.admin";

        private class PluginConfig
        {
            [JsonProperty("Block range (meters) for admins aiming at a site")]
            public float BlockRange = 15f;
        }

        private PluginConfig config;
        private StoredData data;

        private class StoredData
        {
            public HashSet<ulong> Blocked = new HashSet<ulong>();
        }

        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"] = "This sam site is now blocked from firing.",
                ["Unblocked"] = "This sam site can fire again.",
                ["NoSite"] = "Look at a sam site within {0} meters.",
                ["Denied"] = "You do not have permission to use this command."
            }, 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(PermAdmin, this);
            LoadData();
        }

        private void LoadData()
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
            if (data.Blocked == null) data.Blocked = new HashSet<ulong>();
        }

        private void Unload()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }

        private object CanSamSiteShoot(SamSite samSite)
        {
            if (samSite == null || data == null) return null;
            if (data.Blocked.Contains(samSite.net.ID.Value)) return false;
            return null;
        }

        [ChatCommand("samsite.block")]
        private void CmdBlock(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                SendReply(player, Msg("Denied", player.UserIDString));
                return;
            }

            SamSite site = FindLookedAtSite(player);
            if (site == null)
            {
                SendReply(player, Msg("NoSite", player.UserIDString, config.BlockRange));
                return;
            }

            ulong id = site.net.ID.Value;
            if (data.Blocked.Contains(id))
            {
                data.Blocked.Remove(id);
                SendReply(player, Msg("Unblocked", player.UserIDString));
            }
            else
            {
                data.Blocked.Add(id);
                SendReply(player, Msg("Blocked", player.UserIDString));
            }
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }

        private SamSite FindLookedAtSite(BasePlayer player)
        {
            Vector3 origin = player.eyes.position;
            Vector3 forward = player.eyes.HeadForward();
            SamSite best = null;
            float bestDist = config.BlockRange;

            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                SamSite site = entity as SamSite;
                if (site == null || site.IsDestroyed) continue;
                Vector3 to = site.transform.position - origin;
                float dist = to.magnitude;
                if (dist > bestDist) continue;
                if (Vector3.Dot(forward, to.normalized) < 0.7f) continue;
                best = site;
                bestDist = dist;
            }

            return best;
        }
    }
}

The plugin lets admins stop specific sam sites from firing without removing ammo or power. CanSamSiteShoot checks a stored set of blocked entity network IDs, loaded from the data file. When the site is blocked, the hook returns false, which is non-null and therefore prevents the shot for that tick while leaving the site intact. A chat command samsite.block toggles the block for the site the admin is looking at (within a 10 m range using a forward ray). Data is written in Unload so blocks survive restarts. Because the hook is hot, it only performs a hash-set lookup and skips all logging.

Common use cases

  • Admin-controlled disarm

    Disable individual sam sites during events or while troubleshooting grief without deleting rockets or cutting power. Toggle the block from an admin chat command.

  • Zone-based safety

    Refuse shots when the site sits inside a protected build zone or near a player event area, keeping automated defenses from firing into controlled spaces.

  • Ammo conservation

    Prevent low-priority sam sites from consuming rockets during quiet hours by checking samSite.inventory and returning a non-null value when only the minimum ammo remains.

Pitfalls

  • Called every weapon tick

    The hook runs whenever a site is ready to fire, which can be several times per second with many sites. Keep the body to cheap checks (hash lookups, simple comparisons) and never log or allocate large objects.

  • Any non-null value blocks

    Returning 0, an empty string, or false all stop the shot. Only null allows firing, so a mistaken return false; from a condition you meant to allow will silently disable the site.

  • Not a target filter

    The hook fires after a target is already selected; it does not let you change which entity is targeted. To influence targeting, use OnSamSiteTarget or OnSamSiteTargetScan instead.

Build a plugin with CanSamSiteShoot

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

Prompt: Admin-controlled disarm using the CanSamSiteShoot 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 CanSamSiteShoot

Generate