Skip to content
RustPluginGenerator

CanCreateWorldProjectile

CanCreateWorldProjectile is called from BasePlayer.CreateWorldProjectile before the game spawns a projectile item entity in the world. Returning a non-null value cancels the spawn.

Signature
object CanCreateWorldProjectile(HitInfo info, ItemDefinition itemDef)

Parameters

Hook parameters
ParameterTypeDescription
infoHitInfoHitInfo describing the hit that produced the projectile (weapon, initiator, damage type, impact position). Use info.InitiatorPlayer or info.Weapon to identify who fired.
itemDefItemDefinitionItemDefinition of the projectile item that would be spawned in the world, e.g. the ammo item definition. Useful for filtering by shortname or itemid.

Return value

Returning any non-null value stops the original method before the projectile item is created, so no world projectile appears. Returning null lets the game continue and spawn the projectile normally.

How it works

The hook fires inside BasePlayer.CreateWorldProjectile, the method that turns a shot into a world projectile item (the recoverable projectile entity left behind, which the game would otherwise create or recycle). It runs before the game computes info.ProjectileVelocity, creates or reuses the item, and spawns the prefab, so blocking here prevents the world projectile entirely without touching damage.

info identifies the shooter through InitiatorPlayer (may be null for non-player sources) and carries the weapon and hit position. itemDef is the projectile item definition, so you can match on itemDef.shortname or itemDef.itemid to target specific ammo types. Declaring HitInfo and ItemDefinition is the only supported signature; Oxide dispatches to your method only when both arguments match these types.

The call is cheap and fires per projectile creation, not per frame, but it still runs often on busy combat servers. Keep the callback short: a permission check or a shortname comparison, not heavy iteration.

Example plugin: Stop world projectiles for players without a permission

NoWorldProjectiles.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("NoWorldProjectiles", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks world projectile creation for players without permission")]
    public class NoWorldProjectiles : RustPlugin
    {
        private const string PermAllow = "noworldprojectiles.allow";

        private PluginConfig config;
        private readonly Dictionary<ulong, float> lastNotice = new Dictionary<ulong, float>();

        private class PluginConfig
        {
            [JsonProperty("Enabled")]
            public bool Enabled = true;

            [JsonProperty("Only these projectile shortnames (empty = all)")]
            public List<string> Shortnames = new List<string>();

            [JsonProperty("Warn message cooldown (seconds)")]
            public float WarnCooldown = 15f;
        }

        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 allowed to create world projectiles.",
                ["NoPermission"] = "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(PermAllow, this);
        }

        private object CanCreateWorldProjectile(HitInfo info, ItemDefinition itemDef)
        {
            if (!config.Enabled || info == null || itemDef == null) return null;

            if (config.Shortnames.Count > 0 && !config.Shortnames.Contains(itemDef.shortname)) return null;

            BasePlayer player = info.InitiatorPlayer;
            if (player == null || player.IsNpc || !player.IsConnected) return null;
            if (permission.UserHasPermission(player.UserIDString, PermAllow)) return null;

            Notice(player);
            return true;
        }

        private void Notice(BasePlayer player)
        {
            if (config.WarnCooldown <= 0f) return;

            float now = Time.realtimeSinceStartup;
            float last;
            if (lastNotice.TryGetValue(player.userID, out last) && now - last < config.WarnCooldown) return;

            lastNotice[player.userID] = now;
            player.ChatMessage(Msg("Blocked", player.UserIDString));
        }

        [ChatCommand("projblock")]
        private void CmdToggle(BasePlayer player, string command, string[] args)
        {
            if (player == null || !player.IsAdmin)
            {
                if (player != null) player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            config.Enabled = !config.Enabled;
            SaveConfig();
            player.ChatMessage(config.Enabled ? "World projectiles blocked." : "World projectiles allowed.");
        }

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

The plugin registers a single permission noworldprojectiles.allow. In CanCreateWorldProjectile it looks up the firing player from info.InitiatorPlayer and skips the hook for NPCs or unconnected players so default behavior is preserved. If the player does not hold the permission, the method returns true, which tells Oxide to cancel the original spawn, so no recoverable projectile item is left on the ground. The config lets admins toggle the feature, restrict it to a shortname list, and override the chat warning. A short per-player cooldown keeps the chat feedback from spamming when a player fires an entire magazine.

Common use cases

  • Restrict arrow and rocket recovery

    Deny world projectile creation for specific ammo shortnames so projectiles are consumed and never recoverable, changing the loot economy on raid-heavy servers.

  • VIP-only projectile recovery

    Gate projectile spawning behind a permission so only paying players leave recoverable ammo on the ground.

  • Anti-macro projectile spam

    Combine with a rate counter per player and block projectile creation above a threshold to cut down on farming macros.

Pitfalls

  • This is not damage control

    Returning a value here only stops the world projectile item from spawning. The shot still damages whatever it hits; use OnEntityTakeDamage for damage changes.

  • InitiatorPlayer can be null

    Projectiles fired by traps, turrets or NPCs have no player initiator. Always null-check before reading permissions, otherwise you risk a NullReferenceException in a hot path.

  • Broadcasting on every shot

    This hook can fire dozens of times per second on a busy server. Avoid Puts or Broadcast inside it; throttle any player-facing message with a cooldown dictionary.

Build a plugin with CanCreateWorldProjectile

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

Prompt: Restrict arrow and rocket recovery using the CanCreateWorldProjectile 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 CanCreateWorldProjectile

Generate