OnSprayRemove
OnSprayRemove is called when a player requests to clear a spray they placed, before the spray entity is killed. Returning a non-null value blocks the removal.
object OnSprayRemove(SprayCanSpray sprayCanSpray, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
sprayCanSpray | SprayCanSpray | The spray entity the player is trying to remove. It is a world entity (SprayCanSpray), so you can read its position, net.ID, owner and skin; it is not the can item itself. |
player | BasePlayer | The BasePlayer who sent the clear request. Use it for permission checks, feedback messages and ownership comparisons against the spray. |
Return value
Return null to let the game continue and kill the spray entity. Return any non-null value to skip the original Kill() call, leaving the spray in the world; the returned object itself is not used for anything.
How it works
The hook fires inside SprayCanSpray.Server_RequestWaterClear, which the client triggers when a player selects the water-clear option on a spray. Before the call, the game checks that the request player is non-null and that the clear menu is actually available to them; only then does Oxide invoke OnSprayRemove with the spray entity and the requesting player. If your hook returns null, the original code calls Kill() on the spray, removing it from the world and refunding/consuming nothing extra. If you return any non-null object, the Kill() call is skipped and the spray stays in place. Use the sprayCanSpray parameter to inspect the entity (position, owner, net.ID) and player to run permission checks or send chat feedback. Because the hook is per-request, it only fires on player-driven clears, not on server-side kills, decay, or admin removal. There is no ability to substitute a different spray entity or mutate the removal; the only decision is allow or block.
Example plugin: Block non-owners from clearing sprays with owner-only permission
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("SprayClearGuard", "RustPluginGenerator", "1.0.0")]
[Description("Restricts who can clear spray decals, optionally to owners and permitted players.")]
public class SprayClearGuard : RustPlugin
{
private const string PermUse = "sprayclearguard.use";
private PluginConfig config;
public class PluginConfig
{
[JsonProperty("Allow admins to bypass")]
public bool AllowAdmins { get; set; } = true;
[JsonProperty("Require ownership when permission is held")]
public bool RequireOwnershipForPermission { get; set; } = 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>
{
["NotAllowed"] = "You are not allowed to clear this spray.",
["NotOwner"] = "Only the owner of this spray can clear it."
}, 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(PermUse, this);
}
private object OnSprayRemove(SprayCanSpray sprayCanSpray, BasePlayer player)
{
if (sprayCanSpray == null || player == null) return null;
if (config.AllowAdmins && player.IsAdmin) return null;
bool hasPerm = permission.UserHasPermission(player.UserIDString, PermUse);
if (!hasPerm)
{
player.ChatMessage(Msg("NotAllowed", player.UserIDString));
return true;
}
if (config.RequireOwnershipForPermission && sprayCanSpray.OwnerID != 0 && sprayCanSpray.OwnerID != player.userID.Get())
{
player.ChatMessage(Msg("NotOwner", player.UserIDString));
return true;
}
return null;
}
}
}The plugin registers a sprayclearguard.use permission. In OnSprayRemove it verifies both arguments are non-null, then checks whether the requesting player has the permission and whether they are the spray's owner (or an admin). If either check fails, it sends a localized chat message and returns true to prevent the spray from being killed. Owners and admins fall through and return null, so the default removal proceeds. The config exposes a toggle for admin bypass and an ownership requirement, and messages are handled through the language file so server owners can translate them. No timers or UI are needed because the decision is synchronous and per request.
Common use cases
Owner-only spray clearing
Prevent teammates, raiders or random players from wiping other players' sprays by blocking the request unless the caller matches the spray's owner.
Permission-gated removal
Require a specific Oxide permission before allowing a spray to be cleared, giving donors or staff an exclusive tool.
Audit removal requests
Record who requested a clear and where, so staff can review griefing reports without changing the actual removal behavior.
Pitfalls
Return null for default behavior
Many plugins mistakenly return
falseto allow removal; any non-null value blocks the kill. Only return null to let the spray be removed.OwnerID belongs to the spray entity
Compare the player's userID against
sprayCanSpray.OwnerID, not against the player's team membership. Team sprays are still owned by the placing player.Hook only fires on client clear requests
Admin commands, decay, entity cleanup and server restarts remove sprays without firing OnSprayRemove, so do not rely on it for persistence or audit trails of every removal.
Build a plugin with OnSprayRemove
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 OnSprayRemove
Generate