OnSignContentCopied
OnSignContentCopied is called after SignContent.CopyInfoToSign copies a player's sign artwork and edit history onto a sign entity.
void OnSignContentCopied(SignContent signContent, ISignage signage, IUGCBrowserEntity browserEntity)Parameters
| Parameter | Type | Description |
|---|---|---|
signContent | SignContent | The SignContent instance holding the texture data and edit history that is being copied onto the signage. |
signage | ISignage | The target ISignage entity (sign, photo frame) receiving the content; cast to BaseEntity or BaseNetworkable when you need its position or ID. |
browserEntity | IUGCBrowserEntity | The IUGCBrowserEntity view of the same entity, exposing the editing history; usually only needed when you inspect the copy source. |
Return value
The hook is injected with a continue behavior and CopyInfoToSign returns void, so any return value you produce is ignored and the original copy always completes.
How it works
The hook fires at the end of SignContent.CopyInfoToSign, after the target signage's EditingHistory has been cleared and refilled with the entries from the source SignContent. That method is the transfer step in Rust's sign workflow: a player paints in the sign editor and the server writes the resulting texture and history onto the world entity, so this hook runs once per completed copy. Because it fires after the data is already written, it is a notification hook, not a gate.
signContent gives you the source data (texture, edit history). s is the target signage, and b is the same entity seen through IUGCBrowserEntity. Both interface parameters can be cast to BaseEntity or BaseNetworkable; the interface itself has few usable members. Declaring a more specific type on the interface parameters is not practical, but declaring SignContent exactly is required for Oxide to dispatch the call.
Use it to record who painted which sign, audit vandalism on public signs, or trigger a reward when a player completes artwork. Keep the handler cheap: it can fire on every save of a sign editor session, so avoid chat spam or heavy entity scans.
Example plugin: Grant a reward when a player paints a sign
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Sign Paint Reward", "RustPluginGenerator", "1.0.0")]
[Description("Gives players a scrap reward the first time they finish painting a sign, with a cooldown.")]
public class SignPaintReward : RustPlugin
{
private const string PermReward = "signpaintreward.use";
private PluginConfig config;
private readonly Dictionary<string, double> lastReward = new Dictionary<string, double>();
private class PluginConfig
{
[JsonProperty("Reward amount")]
public int RewardAmount = 25;
[JsonProperty("Reward item shortname")]
public string RewardItem = "scrap";
[JsonProperty("Cooldown seconds")]
public double CooldownSeconds = 600.0;
}
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>
{
["Reward"] = "Nice artwork! You received {0} x {1}.",
["Cooldown"] = "Sign reward is on cooldown."
}, 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(PermReward, this);
}
private void OnSignContentCopied(SignContent signContent, ISignage signage, IUGCBrowserEntity browserEntity)
{
if (signContent == null || signage == null) return;
var entity = signage as BaseEntity;
if (entity == null || entity.OwnerID == 0) return;
var player = BasePlayer.FindByID(entity.OwnerID);
if (player == null || !player.IsConnected || player.IsAdmin) return;
if (!permission.UserHasPermission(player.UserIDString, PermReward)) return;
double now = UnityEngine.Time.realtimeSinceStartup;
double last;
if (lastReward.TryGetValue(player.UserIDString, out last) && now - last < config.CooldownSeconds)
{
return;
}
var item = ItemManager.CreateByName(config.RewardItem, config.RewardAmount);
if (item == null) return;
if (!player.inventory.GiveItem(item))
{
item.Drop(player.transform.position, Vector3.zero);
}
lastReward[player.UserIDString] = now;
player.ChatMessage(Msg("Reward", player.UserIDString, config.RewardAmount, item.info.displayName.english));
}
}
}The plugin hooks OnSignContentCopied, casts the signage to a BaseEntity and looks up the owner. If the owner is a connected, non-admin player and their cooldown has expired, it awards scrap through ItemManager.CreateByName and GiveItem, updates the cooldown, and sends a localized chat message. A dictionary keyed by UserIDString tracks per-player cooldowns, and the reward amount comes from a config file so admins can tune it. All player-facing text goes through the language file, and the handler never prints to console.
Common use cases
Reward creative players
Hand out scrap or a currency item the first time a player finishes painting a sign, with a per-player cooldown to prevent farming.
Audit public artwork
Record the owner ID, position, and timestamp of every sign content copy so staff can trace who painted an offensive or rule-breaking image.
Flag suspicious sign edits
Compare the sign owner with the editing history entries and warn staff when a sign is being overwritten by a different player.
Pitfalls
Fires after the write
The texture and history are already on the entity when the hook runs, so you cannot reject or modify a copy here. Blocking has to happen earlier in the sign editor flow.
Interface parameters are thin
ISignage and IUGCBrowserEntity expose little, so cast
sorbto BaseEntity/BaseNetworkable before reading OwnerID or transform.Can fire repeatedly
Each copy onto the entity triggers the hook, so cooldowns and cheap handlers keep rewards and logs from spamming.
Build a plugin with OnSignContentCopied
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 OnSignContentCopied
Generate