OnClientProjectileEffectCreate
OnClientProjectileEffectCreate is called on the server right before BaseProjectile sends a clientside projectile effect to a connection. Returning a non-null value suppresses that effect for the shot.
object OnClientProjectileEffectCreate(Network.Connection sourceConnection, BaseProjectile projectile, string prefabName)Parameters
| Parameter | Type | Description |
|---|---|---|
sourceConnection | Network.Connection | Network.Connection of the player whose client receives the projectile effect. May be null when the effect is broadcast or originates from a non-player source. |
projectile | BaseProjectile | The BaseProjectile firing the shot, e.g. the held weapon entity. Use it to read the owner, item, or prefab name. |
prefabName | string | Asset path of the client-side projectile effect prefab that would be sent to clients, such as the tracer or bullet visual. |
Return value
Return null (or nothing) to let the effect be created and sent normally. Return any non-null value to skip the effect for that connection entirely, so no clientside projectile visual is produced from this call.
How it works
The hook fires inside BaseProjectile.CreateProjectileEffectClientside, the method that builds the reusable projectile Effect and ships it to one or more client connections. It runs for each shot that produces a clientside visual, so automatic weapons call it many times per second. sourceConnection is the target connection (null for broadcasts or non-player sources), projectile is the weapon entity doing the firing, and prefabName is the effect prefab about to be used. Returning anything non-null stops the original method before the effect is initialized, so the clients never see that projectile visual while damage and server-side ballistics are unaffected. Because it is a high-frequency hook, do not print or log from it. Keep the check cheap: a permission or flag lookup on the weapon owner. You can narrow the parameter to a subclass such as BaseProjectile already covers most guns; if you only care about one weapon type, check projectile.ShortPrefabName instead of narrowing further, since Oxide dispatch only matches when the argument is an instance of the declared type.
Example plugin: Hide bullet tracers for players with a permission
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("SilentTracers", "RustPluginGenerator", "1.0.0")]
[Description("Suppresses clientside bullet tracer effects for players holding a permission.")]
public class SilentTracers : RustPlugin
{
private const string PermHide = "silenttracers.hide";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Broadcast message when toggled")]
public bool NotifyToggle = true;
}
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>
{
["Enabled"] = "Tracer effects are now hidden for you.",
["Disabled"] = "Tracer effects are visible again.",
["NoPermission"] = "You do not have permission to use this command."
}, this);
}
private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);
private void Init()
{
permission.RegisterPermission(PermHide, this);
}
[ChatCommand("tracers")]
private void CmdTracers(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermHide))
{
if (!player.IsAdmin) return;
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
}
[ConsoleCommand("silenttracers.toggle")]
private void CcmdToggle(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null) return;
if (permission.UserHasPermission(player.UserIDString, PermHide))
{
permission.RevokeUserPermission(player.UserIDString, PermHide);
if (config.NotifyToggle) player.ChatMessage(Msg("Disabled", player.UserIDString));
}
else
{
permission.GrantUserPermission(player.UserIDString, PermHide, this);
if (config.NotifyToggle) player.ChatMessage(Msg("Enabled", player.UserIDString));
}
}
private object OnClientProjectileEffectCreate(Network.Connection sourceConnection, BaseProjectile projectile, string prefabName)
{
if (projectile == null) return null;
var owner = projectile.GetOwnerPlayer();
if (owner == null) return null;
if (permission.UserHasPermission(owner.UserIDString, PermHide))
{
return true;
}
return null;
}
}
}The plugin registers a permission and checks it against the weapon owner found through projectile.GetOwnerPlayer(). When the owner has silenttracers.hide, the hook returns a non-null value so CreateProjectileEffectClientside never builds the effect for that connection, hiding the bullet tracer from the shooter and nearby clients. Damage is untouched because the check happens after the shot is resolved server-side. A chat command toggles the permission so admins and testers can switch visuals without editing files. Null checks guard the connection and projectile so console or NPC-driven effects pass through unharmed.
Common use cases
Staff-only silent weapons
Give admins a permission that suppresses tracers so they can shoot test targets without drawing attention from players watching the tracer line.
Reduce clientside visual load
Suppress projectile effects for high-rate weapons in crowded events to keep client frame rates stable while keeping damage identical.
Cinematic or PvE servers
Disable tracers on custom game modes where the visual does not fit the theme, using a prefab whitelist on
prefabName.
Pitfalls
It fires for every shot
Automatic weapons can call this hook many times a second per player. Avoid allocations, permission checks with string concatenation, or any printing inside the hook body.
The connection can be null
Broadcast or non-player effects pass a null
sourceConnection. Check the projectile owner instead of assuming the connection exists.Cosmetic only
Returning non-null removes the visual effect, not the bullet. Damage, hit registration and server-side ballistics still apply.
Build a plugin with OnClientProjectileEffectCreate
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 OnClientProjectileEffectCreate
Generate