Skip to content
RustPluginGenerator

OnEntityReskin

OnEntityReskin is called when a player applies a spray can skin to an entity, before the game resolves redirects and commits the change. Returning a non-null value cancels the reskin.

Signature
object OnEntityReskin(BaseEntity entity, ulong skinId, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
entityBaseEntityThe BaseEntity the spray can is being used on. Can be a BuildingBlock, a Deployable such as a door, or any other entity the spray tool accepts; check its type before acting.
skinIdulongThe resolved skin id the game is about to apply, already mapped through redirects. 0 usually means the player is removing the current skin rather than adding one.
playerBasePlayerThe BasePlayer holding the spray can and performing the reskin. May be null in edge cases, so guard before using it.

Return value

Returning null lets the reskin continue normally. Returning any non-null object stops the original method before the skin is applied, so the entity keeps its current look and no resources or items are consumed by that spray action.

How it works

The hook fires inside SprayCan.ChangeItemSkin, after the game has validated the target entity and looked up the resulting skin id, but before it resolves spray redirects and rewrites the entity skin. It fires once per spray action: the player holds a spray can, aims at a building block or deployable and confirms the reskin. entity is the object being sprayed, skinId is the already-resolved skin id (0 when the player is stripping the skin), and player is the spraying player.

Declaring entity as a more specific type such as BuildingBlock, Door or BaseCombatEntity narrows the hook so Oxide only calls your method when the target matches. If you declare Door and the player sprays a wall, your method is skipped entirely and the reskin proceeds.

Practical uses: a permission-gated skin system, blocking skins on specific entities, charging currency through a referenced plugin, or logging who applied which skin. Because the hook runs before the change is committed, returning non-null is a clean veto.

Example plugin: Gate spray can skins behind a permission

ReskinPermissionGate.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("ReskinPermissionGate", "RustPluginGenerator", "1.0.0")]
    [Description("Requires a permission to apply or remove spray can skins on entities.")]
    public class ReskinPermissionGate : RustPlugin
    {
        private const string PermUse = "reskinpermissiongate.use";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Block skins on deployables")]
            public bool BlockDeployables { get; set; } = true;

            [JsonProperty("Notify unauthorized players")]
            public bool Notify { 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>
            {
                ["NoPermission"] = "You do not have permission to spray skins.",
                ["DeployableBlocked"] = "Skins are not allowed on deployable entities."
            }, this);
        }

        private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        private object OnEntityReskin(BaseEntity entity, ulong skinId, BasePlayer player)
        {
            if (entity == null || player == null) return null;
            if (player.IsAdmin) return null;

            if (!permission.UserHasPermission(player.UserIDString, PermUse))
            {
                if (config.Notify)
                {
                    player.ChatMessage(Msg("NoPermission", player.UserIDString));
                }
                return false;
            }

            if (config.BlockDeployables && !(entity is BuildingBlock))
            {
                if (config.Notify)
                {
                    player.ChatMessage(Msg("DeployableBlocked", player.UserIDString));
                }
                return false;
            }

            return null;
        }
    }
}

The plugin registers a single permission, reskinpermissiongate.use, and implements OnEntityReskin to check it. When a player sprays an entity, the hook looks up their permission; if they lack it, the hook shows a localized chat message and returns false (non-null) to cancel the reskin. Players with permission get null, so the game applies the skin as usual. The example also respects an admin bypass so staff can work without granting the permission. Optional config blocks reskinning of deployed entities (doors, boxes) while still allowing building blocks, which keeps base cosmetics consistent. It doubles as a skeleton for a currency-charged skin system.

Common use cases

  • Permission-gated spray skins

    Only players with a specific permission may apply or remove spray can skins. The hook cancels any attempt from unauthorized players before the change lands.

  • Charge for skins via Economics

    Use the hook to check the player's balance with a referenced Economics plugin and return non-null when they cannot afford the skin, then deduct the cost separately once the reskin succeeds.

  • Restrict which entity types can be sprayed

    Narrow the hook to BuildingBlock or Door and cancel reskins on deployables to keep player-made structures uniform, while still allowing wall and floor skins.

Pitfalls

  • Skin id 0 means removal

    When a player strips a skin, skinId is 0. If you gate reskins behind a permission or a cost, decide whether removing a skin also needs gating, otherwise players can always revert cosmetics for free.

  • Player can be null

    Guard player before reading UserIDString or sending chat. Some code paths can fire the hook without a connected player object.

  • Returning true still cancels

    Any non-null return stops the original method. Returning true does not mean 'allow'; use null to allow the reskin and a non-null value such as false to block it.

Build a plugin with OnEntityReskin

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

Prompt: Permission-gated spray skins using the OnEntityReskin 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 OnEntityReskin

Generate