Skip to content
RustPluginGenerator

OnRfBroadcasterRemoved

OnRfBroadcasterRemoved is called when an RF broadcaster is removed from a frequency in RFManager, after the removal succeeds and before the manager updates listener state. It fires for every successful removal, whether from a player toggling a device or an entity being destroyed.

Signature
void OnRfBroadcasterRemoved(IRFObject obj, int frequency)

Parameters

Hook parameters
ParameterTypeDescription
objIRFObjectThe IRFObject that stopped broadcasting. It is the same object passed to OnRfBroadcasterAdd, so you can match it to trackers keyed by the object or its net ID.
frequencyintThe RF frequency (0-65535) the broadcaster was removed from. Note that a single entity can broadcast on more than one frequency, so always pair this with obj.

Return value

Return value is ignored. The original method continues normally and returns void, so the frequency's broadcasting flag and listener updates are applied regardless of what your hook returns.

How it works

This hook fires inside RFManager.RemoveBroadcaster after the broadcaster set for the frequency has had obj removed successfully. If the object was not in that set, the removal fails and the hook is not called. The call happens before the manager flips _isFrequencyBroadcasting[frequency] to false and calls UpdateListenersForFrequency, so the RF state you observe at hook time is still the post-removal set with listeners not yet notified.

Use obj to identify the device (for example by capturing its net ID before the removal) and frequency to know which channel went silent. Broadcaster removal happens when a player turns off a transmitter, changes an RF frequency on a device, or when the entity carrying the broadcaster is destroyed or killed.

Narrowing the parameter type works because Oxide checks that each argument matches your declared parameter type. Declaring obj as a concrete type such as BaseEntity makes Oxide skip the call whenever the removed object is not a BaseEntity, so use IRFObject unless you only care about a subset.

Keep the body cheap: it can fire during entity teardown and server save loops.

Example plugin: Alert when a transmitter leaves a watched frequency

RfBroadcasterWatch.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Rf Broadcaster Watch", "RustPluginGenerator", "1.0.0")]
    [Description("Warns online players when a transmitter leaves a watched RF frequency")]
    public class RfBroadcasterWatch : RustPlugin
    {
        private const string PermNotify = "rfbroadcasterwatch.notify";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Watched frequencies")]
            public List<int> WatchedFrequencies = new List<int> { 4747 };

            [JsonProperty("Minimum seconds between alerts per frequency")]
            public float AlertCooldown = 30f;
        }

        private readonly Dictionary<int, float> lastAlert = new Dictionary<int, float>();

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null || config.WatchedFrequencies == 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>
            {
                ["Alert"] = "Transmitter on frequency {0} stopped broadcasting (device at {1})."
            }, 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(PermNotify, this);
        }

        private void OnRfBroadcasterRemoved(IRFObject obj, int frequency)
        {
            if (!config.WatchedFrequencies.Contains(frequency)) return;

            float now = UnityEngine.Time.realtimeSinceStartup;
            if (lastAlert.TryGetValue(frequency, out float last) && now - last < config.AlertCooldown) return;
            lastAlert[frequency] = now;

            string where = "unknown location";
            BaseEntity entity = obj as BaseEntity;
            if (entity != null && !entity.IsDestroyed)
            {
                Vector3 pos = entity.transform.position;
                where = $"{Mathf.RoundToInt(pos.x)}, {Mathf.RoundToInt(pos.z)}";
            }

            string message = Msg("Alert", null, frequency, where);
            NextTick(() =>
            {
                foreach (BasePlayer player in BasePlayer.activePlayerList)
                {
                    if (player == null || !player.IsConnected) continue;
                    if (!permission.UserHasPermission(player.UserIDString, PermNotify) && !player.IsAdmin) continue;
                    player.ChatMessage(message);
                }
            });
        }

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

The plugin keeps a configurable list of watched frequencies (default 4747) and notifies team members when a transmitter leaves one of them, which usually means a device was destroyed or turned off.

On load it registers the hook by declaring OnRfBroadcasterRemoved and parses the config. Because the removed object is not guaranteed to be a BaseEntity at hook time, the handler resolves it safely with as BaseEntity and builds a short label from position and owner ID. It does not store broadcaster state permanently; instead it records the net ID and formatted message, then sends the alert on the next tick so the removal finishes first. Chat text goes through the language file, and the watched frequency list is read from oxide/config.

Common use cases

  • Log removed transmitters

    Record every broadcaster removal with frequency and object ID to a running server log held in oxide/data for later auditing of RF abuse.

  • Security radio monitoring

    Alert guards when a transmitter on a monitored security frequency goes offline, which can indicate a turret or door controller was destroyed.

  • Balancing RF devices

    Reward or punish players for managing devices by tracking how long a broadcaster stayed on a frequency between add and remove events.

Pitfalls

  • Not called when the object was not registered

    The hook only fires when broadcasterSet.Remove(obj) returns true. Removing an object from a frequency it never broadcast on is silent, so do not expect a paired remove for every add you saw.

  • One object, multiple frequencies

    A single device can have broadcasters on several frequencies. Key your tracking data by object and frequency together, otherwise you will close the wrong entry.

  • Fires during entity destruction

    Removal often happens while an entity is being killed. Avoid touching destroyed components or spawning entities from this hook; queue work with NextTick if needed.

Build a plugin with OnRfBroadcasterRemoved

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

Prompt: Log removed transmitters using the OnRfBroadcasterRemoved 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 OnRfBroadcasterRemoved

Generate