OnRfListenerAdded
OnRfListenerAdded is called after an object has successfully registered as a listener on an RF frequency through RFManager.AddListener. It only fires when the listener was actually added, so it never reports duplicates.
void OnRfListenerAdded(IRFObject obj, int frequency)Parameters
| Parameter | Type | Description |
|---|---|---|
obj | IRFObject | The IRFObject that was just registered as a listener on the frequency. Cast it to BaseEntity (or a specific type such as RFReceiver, Door or BaseOven) to inspect or act on the entity. |
frequency | int | The RF channel the listener was added to. Useful to allow or restrict specific channels and to correlate the listener with broadcasters on the same frequency. |
Return value
The return value is ignored. The listener has already been added by the time the hook runs, so returning a value cannot undo the registration or change the frequency. To react to a listener being removed, use OnRfListenerRemove or OnRfListenerRemoved.
How it works
The hook fires inside RFManager.AddListener, after the game has confirmed the object is now part of that frequency's listener set. Registration happens when entities that receive RF signals become active: a powered RF receiver, an RF-triggered door or garage door, a base oven or other RF-controllable deployable. If the object was already listening on that frequency, GetListenerSet(frequency).Add(obj) returns false and neither the signal update nor this hook runs.
obj is the listener interface itself; cast it to BaseEntity to get OwnerID, position, prefab name or the building privilege it sits in. frequency is the channel number, which lets you allow only a set of frequencies, notify owners, or track channel usage per entity.
Because the parameter is IRFObject, you cannot narrow the dispatch by declaring a more specific type; instead filter with an as cast inside the hook. The hook can fire repeatedly for the same entity, since every activation cycle re-adds the listener. Keep the body cheap: it runs for every listener registration, including mass activations after a restart.
Example plugin: Log and alert on restricted RF channel listeners
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Rf Channel Monitor", "RustPluginGenerator", "1.0.0")]
[Description("Warns players when an RF listener registers on a restricted channel near their tracked position")]
public class RfChannelMonitor : RustPlugin
{
private const string PermBypass = "rfchannelmonitor.bypass";
private PluginConfig config;
private StoredData data;
private class PluginConfig
{
[JsonProperty("Restricted frequencies")]
public List<int> RestrictedFrequencies = new List<int> { 4242, 7777 };
[JsonProperty("Alert radius (meters)")]
public float AlertRadius = 50f;
[JsonProperty("Tracked position lifetime (seconds)")]
public int TrackLifetime = 120;
}
private class StoredData
{
[JsonProperty("Tracked players")]
public Dictionary<ulong, TrackedPlayer> Tracked = new Dictionary<ulong, TrackedPlayer>();
}
private class TrackedPlayer
{
[JsonProperty("Position")]
public Vector3 Position;
[JsonProperty("Timestamp")]
public int Timestamp;
}
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>
{
["Alert"] = "RF listener detected on restricted channel {0} at {1}. A device near you is listening.",
["Owner"] = "Your device now listens on restricted channel {0}."
}, 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(PermBypass, this);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void OnPlayerSleepEnded(BasePlayer player)
{
if (player == null || player.IsNpc) return;
data.Tracked[player.userID] = new TrackedPlayer
{
Position = player.transform.position,
Timestamp = Facepunch.Math.Epoch.Current
};
}
private void OnRfListenerAdded(IRFObject obj, int frequency)
{
if (config == null || !config.RestrictedFrequencies.Contains(frequency)) return;
var entity = obj as BaseEntity;
if (entity == null || entity.IsDestroyed) return;
var owner = BasePlayer.FindByID(entity.OwnerID);
if (owner != null && owner.IsConnected && !permission.UserHasPermission(owner.UserIDString, PermBypass))
{
owner.ChatMessage(Msg("Owner", owner.UserIDString, frequency));
return;
}
Vector3 position = entity.transform.position;
int now = Facepunch.Math.Epoch.Current;
var stale = new List<ulong>();
foreach (var pair in data.Tracked)
{
if (now - pair.Value.Timestamp > config.TrackLifetime)
{
stale.Add(pair.Key);
continue;
}
if (Vector3.Distance(pair.Value.Position, position) > config.AlertRadius) continue;
var player = BasePlayer.FindByID(pair.Key);
if (player == null || !player.IsConnected) continue;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) continue;
player.ChatMessage(Msg("Alert", player.UserIDString, frequency,
position.ToString("F0")));
}
foreach (var id in stale) data.Tracked.Remove(id);
}
}
}The plugin keeps a configurable list of restricted RF frequencies. When OnRfListenerAdded fires it casts the IRFObject to a BaseEntity, resolves the owning player and checks the channel against the list. Allowed listeners are ignored. For a restricted channel the entity's position is compared with each owner that has been seen recently (stored with a timestamp from Facepunch.Math.Epoch.Current), and if the listener sits within the configured radius of another player's tracked position, that player receives a localized warning with the channel and coordinates. Data is written to oxide/data on unload and the tracked table is pruned of stale entries on each check.
Common use cases
Reserve channels for admins or events
Compare
frequencyagainst a configured allow-list and warn or log when an entity registers on a reserved channel, for example 4242 used by admin-controlled doors.Notify owners of RF-controlled devices
Cast
objtoBaseEntity, readOwnerIDand send the owner a message confirming which channel their receiver, door or oven is listening on.Detect proximity RF relays
Track listener positions per frequency and flag when a listener is placed inside another player's base, which can indicate a remote trigger placed during a raid.
Pitfalls
The hook repeats on every activation
Whenever an RF object is re-enabled,
AddListenerruns again and fires the hook. Deduplicate bynet.IDand frequency if you send one-time notifications, otherwise owners get spammed.Can't narrow by parameter type
The parameter is
IRFObject, not a concrete entity type, so declaring something stricter would never match. Do the cast toBaseEntityorDoorinside the method and null-check the result.Fires during load and mass activation
After a restart or map load many listeners register at once. Avoid expensive work (entity scans, chat broadcasts to everyone) unless it is gated by a config flag or a small frequency filter.
Build a plugin with OnRfListenerAdded
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 OnRfListenerAdded
Generate