OnOpenVendingAdmin
OnOpenVendingAdmin is called after a player successfully opens the admin panel of a vending machine, right after the server sends the CLIENT_OpenAdminMenu RPC and opens the 'notCustomerPanel' shop view.
void OnOpenVendingAdmin(VendingMachine vendingMachine, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
vendingMachine | VendingMachine | The VendingMachine entity whose admin UI is being opened. Use it to read the shop name, network ID, owner or position. |
player | BasePlayer | The BasePlayer who is opening the admin panel. This is the player who passed the CanPlayerAdmin check (owner or admin). |
Return value
The return value is ignored because the injection is a plain call on a void method. Returning anything (or nothing) has no effect on the original code path, the admin panel still opens.
How it works
The hook fires inside VendingMachine.RPC_OpenAdmin, the server RPC a client sends when it asks to manage a shop. The game first resolves msg.player, verifies with CanPlayerAdmin(player) (shop owner, or an admin), then calls OpenShop(player, "notCustomerPanel"), sends CLIENT_OpenAdminMenu to that player, and finally calls the hook. Because it runs only after the permission check, the hook never fires for players who are not allowed to administrate the machine.
Both parameters are useful: vendingMachine identifies the shop entity (its net.ID.Value, OwnerID, position, or attached data such as the shop name), and player is the administrator who just got the panel. This is a good point to start a temporary UI, log staff access to shops, or notify owners that someone is editing their machine.
The hook is void, so it is not a gate. To block the admin panel you must use a hook that fires before the check, such as CanAdministerVending. CanPlayerAdmin itself is not an Oxide hook. If the machine is destroyed during the same frame, guarding vendingMachine and player with null checks is still good practice.
Example plugin: Log staff access to vending machine admin panels
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Vending Admin Audit", "RustPluginGenerator", "1.0.0")]
[Description("Logs every successful vending machine admin panel open and notifies staff.")]
public class VendingAdminAudit : RustPlugin
{
private const string PermNotify = "vendingadminaudit.notify";
private PluginConfig config;
private AuditData data;
private Timer flushTimer;
private class PluginConfig
{
[JsonProperty("Maximum stored records")]
public int MaxRecords = 500;
[JsonProperty("Notify permitted players")]
public bool NotifyStaff = true;
}
private class AuditEntry
{
public string AdminName;
public string AdminId;
public string ShopName;
public ulong MachineId;
public string Position;
public string Timestamp;
}
private class AuditData
{
public List<AuditEntry> Entries = new List<AuditEntry>();
}
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>
{
["AdminOpened"] = "{0} ({1}) opened the admin panel of '{2}' at {3}.",
["NoName"] = "Unnamed shop"
}, 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);
data = Interface.Oxide.DataFileSystem.ReadObject<AuditData>(Name) ?? new AuditData();
}
private void OnServerInitialized()
{
flushTimer = timer.Every(60f, SaveData);
}
private void OnOpenVendingAdmin(VendingMachine vendingMachine, BasePlayer player)
{
if (vendingMachine == null || player == null) return;
string shopName = string.IsNullOrEmpty(vendingMachine.shopName) ? Msg("NoName") : vendingMachine.shopName;
string pos = vendingMachine.transform.position.ToString("F0");
data.Entries.Add(new AuditEntry
{
AdminName = player.displayName,
AdminId = player.UserIDString,
ShopName = shopName,
MachineId = vendingMachine.net.ID.Value,
Position = pos,
Timestamp = DateTime.UtcNow.ToString("u")
});
while (data.Entries.Count > config.MaxRecords)
{
data.Entries.RemoveAt(0);
}
if (!config.NotifyStaff) return;
string message = Msg("AdminOpened", null, player.displayName, player.UserIDString, shopName, pos);
foreach (BasePlayer target in BasePlayer.activePlayerList)
{
if (target == null || target == player || !target.IsConnected) continue;
if (target.IsAdmin || permission.UserHasPermission(target.UserIDString, PermNotify))
{
target.ChatMessage(message);
}
}
}
private void SaveData()
{
if (data == null) return;
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void Unload()
{
if (flushTimer != null)
{
flushTimer.Destroy();
flushTimer = null;
}
SaveData();
}
}
}The plugin writes one line to oxide/data/VendingAdminAudit for every successful admin-panel open and pushes a short notice to staff who have the vendingadminaudit.notify permission. OnOpenVendingAdmin reads the machine's shop name via the public shopName field, falls back to a localized placeholder, and records the admin's display name, Steam ID, machine network ID and world position. Records are trimmed to the configured limit, saved on unload and flushed every 60 seconds with a repeating timer. Because the hook fires after the game's own CanPlayerAdmin check, only real administrators (owner or staff) ever appear in the log.
Common use cases
Audit trail for shop edits
Store who opened which vending machine admin panel and when, so owners can see if a teammate or server staff changed prices in their shops.
Staff activity overview
Broadcast a message to online admins when a moderator opens a player shop, so duplicate edits during an event can be avoided.
Owner reminder on admin open
Send the shop owner a chat or Discord-relayed notice that their machine is being edited, along with the machine name and position.
Pitfalls
It cannot block the panel
The hook runs after
CanPlayerAdmin,OpenShopand the admin RPC. UseCanAdministerVendingorCanAccessVendingMachineif you need to deny access.Not a purchase event
This fires for the admin UI only. Customer opens use
OnVendingShopOpen/OnVendingShopOpened, and sales useOnBuyVendingItemorOnVendingTransaction.Keep it cheap
It is a rare hook so light logging is fine, but writing the data file directly on every call causes unnecessary disk I/O. Batch saves with a timer or on unload.
Build a plugin with OnOpenVendingAdmin
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 OnOpenVendingAdmin
Generate