OnToggleVendingBroadcast
OnToggleVendingBroadcast is called after a player changes a vending machine's broadcast (map marker) setting, before the map marker is refreshed. It fires inside VendingMachine.RPC_Broadcast when the client toggles broadcast mode.
void OnToggleVendingBroadcast(VendingMachine vendingMachine, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
vendingMachine | VendingMachine | The VendingMachine whose broadcast setting is being changed. Use it to read the prefab name, position, owner and current shop state. |
player | BasePlayer | The BasePlayer who triggered the RPC. This is the player who toggled the broadcast flag; it can be null on rare network edge cases, so null-check before use. |
Return value
The return value is ignored. The original code continues unconditionally to UpdateMapMarker(), so returning null or any value has no effect on the toggle.
How it works
This hook fires from VendingMachine.RPC_Broadcast, the server RPC the client sends when a player flips the broadcast toggle in the vending machine UI. At that point the Flags.Reserved4 flag has already been applied to the machine, so the new state is readable via vendingMachine.HasFlag(BaseEntity.Flags.Reserved4). The hook runs just before UpdateMapMarker(), which is the step that adds or removes the shop from the map.
vendingMachine gives you the entity itself: position, OwnerID, ShopName, IsBroadcasting and CanBeAdministeredBy are all useful. player is the BasePlayer who sent the RPC. Player-driven toggles always arrive with a player; guard against null anyway since RPC arguments are client-controlled.
You cannot rely on declaring a narrower type for vendingMachine to filter calls, because the hook only fires from VendingMachine. Declaring BasePlayer for the second parameter is fine and matches the injection. The return value is not consulted, so you cannot cancel the toggle from this hook; use it to observe and react.
Example plugin: Track when players toggle shop broadcast
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Vending Broadcast Tracker", "RustPluginGenerator", "1.0.0")]
[Description("Records vending machine broadcast toggles and reports per-player statistics")]
public class VendingBroadcastTracker : RustPlugin
{
private const string PermStats = "vendingbroadcasttracker.stats";
private const int MaxEntries = 200;
private PluginConfig config;
private TrackerData data;
private class PluginConfig
{
[JsonProperty("Chat command enabled")]
public bool ChatCommandEnabled { get; set; } = true;
[JsonProperty("Announce to staff on broadcast enable")]
public bool AnnounceEnable { get; set; } = true;
}
private class TrackEntry
{
[JsonProperty("Steam ID")]
public string UserId { get; set; }
[JsonProperty("Player name")]
public string PlayerName { get; set; }
[JsonProperty("Shop name")]
public string ShopName { get; set; }
[JsonProperty("Broadcasting")]
public bool Broadcasting { get; set; }
[JsonProperty("Position")]
public string Position { get; set; }
[JsonProperty("Unix time")]
public long Time { get; set; }
}
private class TrackerData
{
[JsonProperty("Entries")]
public List<TrackEntry> Entries { get; set; } = new List<TrackEntry>();
[JsonProperty("Toggle counts")]
public Dictionary<string, int> Counts { get; set; } = new Dictionary<string, int>();
}
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 use this command.",
["Stats"] = "You have toggled vending broadcast {0} time(s).",
["StaffAlert"] = "{0} enabled broadcast on shop '{1}' at {2}."
}, 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(PermStats, this);
}
private void OnServerInitialized()
{
data = Interface.Oxide.DataFileSystem.ReadObject<TrackerData>(Name) ?? new TrackerData();
}
private void Unload()
{
if (data != null)
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void OnToggleVendingBroadcast(VendingMachine vendingMachine, BasePlayer player)
{
if (vendingMachine == null || player == null || data == null) return;
bool broadcasting = vendingMachine.HasFlag(BaseEntity.Flags.Reserved4);
string position = vendingMachine.transform.position.ToString("F0");
string shopName = vendingMachine.shopName ?? "unnamed";
data.Entries.Add(new TrackEntry
{
UserId = player.UserIDString,
PlayerName = player.displayName ?? "unknown",
ShopName = shopName,
Broadcasting = broadcasting,
Position = position,
Time = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
});
if (data.Entries.Count > MaxEntries)
data.Entries.RemoveRange(0, data.Entries.Count - MaxEntries);
int count;
data.Counts.TryGetValue(player.UserIDString, out count);
data.Counts[player.UserIDString] = count + 1;
if (!config.AnnounceEnable || !broadcasting) return;
string alert = Msg("StaffAlert", null, player.displayName, shopName, position);
foreach (BasePlayer target in BasePlayer.activePlayerList)
{
if (target != null && target.IsAdmin)
target.ChatMessage(alert);
}
}
[ChatCommand("vending.broadcaststats")]
private void CmdStats(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!config.ChatCommandEnabled)
{
player.ChatMessage("This command is disabled.");
return;
}
if (!permission.UserHasPermission(player.UserIDString, PermStats))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
int count = 0;
if (data?.Counts != null)
data.Counts.TryGetValue(player.UserIDString, out count);
player.ChatMessage(Msg("Stats", player.UserIDString, count));
}
}
}The plugin records every broadcast toggle in an Oxide data file, keeping a per-player toggle count plus a bounded history of recent toggles. OnToggleVendingBroadcast reads the new flag state from Flags.Reserved4 and stores player ID, display name, shop name, position and the resulting state in the entries list. The history is capped at 200 entries by dropping the oldest whenever it grows past that limit. When Announce to staff on broadcast enable is enabled and the new state is broadcasting, active admins receive a chat message with the player name, shop name and position. The chat command vending.broadcaststats prints the caller's own toggle count; it requires the vendingbroadcasttracker.stats permission and can be turned off with the Chat command enabled config option. Data is written on Unload.
Common use cases
Audit broadcast changes for staff
Log who enabled or disabled map broadcast on each vending machine, with the shop name and position, so admins can trace griefing or abuse of map advertising.
Limit map advertising by rank
Pair with
CanAdministerVendingto discourage free broadcasting: when a non-privileged player turns broadcast on, send them a warning that their shop may be hidden on the next restart.Discord or chat alerts for new shops
Notify staff in chat when a player first broadcasts a shop, giving them the position so they can visit and check the offers.
Pitfalls
Return value does nothing
The injection uses a continue return behavior, so you cannot cancel the toggle by returning a non-null value. To prevent a change you would have to revert the flag afterwards and refresh the marker yourself.
Player can be null
RPC arguments come from the client. Null-check
playerandvendingMachinebefore touchingUserIDStringorShopName, and bail out early if either is missing.Flag is already set
The hook fires after the Reserved4 flag is applied, so
IsBroadcastingreports the new value, not the old one. Capture the previous state elsewhere if you need a before/after record.
Build a plugin with OnToggleVendingBroadcast
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 OnToggleVendingBroadcast
Generate