OnSpinWheel
OnSpinWheel is called when a player sends the spin RPC to a SpinnerWheel (the wheel used by the Wheel of Fortune / safe gambling entity) and the game is about to add spin velocity. Returning a non-null value cancels that spin.
object OnSpinWheel(BasePlayer player, SpinnerWheel spinnerWheel)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who sent the spin RPC, taken from the RPCMessage. This is the player interacting with the wheel, not necessarily the owner. |
spinnerWheel | SpinnerWheel | The SpinnerWheel entity being spun. Use it to read its position, owner, net ID or attached storage if you need to scope your rules. |
Return value
Returning null (or any value of a type that cannot stand in for the original return) lets the spin proceed: velocity is increased and colliders are toggled off. Returning a non-null value stops the original method before that code runs, so the wheel never starts spinning.
How it works
The hook fires inside SpinnerWheel.RPC_Spin, after the game has already checked that the player can interact, that spinning is allowed on this wheel, that the player is authorized or anyone may spin, and that the current velocity is not above 15. It runs immediately before the spin velocity is increased and the child entity colliders are disabled, so a call here means the player's spin request passed every vanilla gate. Returning non-null aborts the spin entirely; the wheel does not move and no reward is rolled.
Both parameters are safe to inspect: player is the RPC sender and spinnerWheel is the entity. Because the only call site passes a BasePlayer, narrowing the first parameter to a subclass such as BasePlayer is pointless; there is no meaningful more specific type. The wheel type is fixed, so declare SpinnerWheel exactly. Use this hook for rate limits, paid-spin checks, gambling locks or event gating rather than for rewriting the outcome, which is decided elsewhere.
Example plugin: Cooldown and permission gate for spinning
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Spin Wheel Gate", "RustPluginGenerator", "1.0.0")]
[Description("Gates SpinnerWheel spins behind a permission and a per-player cooldown.")]
public class SpinWheelGate : RustPlugin
{
private const string PermUse = "spinwheelgate.use";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastSpin = new Dictionary<ulong, float>();
private Timer pruneTimer;
private class PluginConfig
{
[JsonProperty("Require permission")]
public bool RequirePermission { get; set; } = true;
[JsonProperty("Cooldown seconds")]
public float CooldownSeconds { get; set; } = 5f;
}
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 are not allowed to spin this wheel.",
["Cooldown"] = "Wait {0} more second(s) before spinning again."
}, 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(PermUse, this);
pruneTimer = timer.Every(30f, PruneCooldowns);
}
private void Unload()
{
pruneTimer?.Destroy();
}
private object OnSpinWheel(BasePlayer player, SpinnerWheel spinnerWheel)
{
if (player == null || spinnerWheel == null) return null;
if (config.RequirePermission && !permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return true;
}
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (config.CooldownSeconds > 0f && lastSpin.TryGetValue(player.userID, out last))
{
float remaining = config.CooldownSeconds - (now - last);
if (remaining > 0f)
{
player.ChatMessage(Msg("Cooldown", player.UserIDString, Mathf.CeilToInt(remaining)));
return true;
}
}
lastSpin[player.userID] = now;
return null;
}
private void PruneCooldowns()
{
if (lastSpin.Count == 0) return;
float threshold = UnityEngine.Time.realtimeSinceStartup - Mathf.Max(config.CooldownSeconds, 1f) * 2f;
var stale = new List<ulong>();
foreach (var entry in lastSpin)
{
if (entry.Value < threshold) stale.Add(entry.Key);
}
foreach (var id in stale) lastSpin.Remove(id);
}
}
}The plugin denies spins when the player lacks the spinwheelgate.use permission and enforces a per-player cooldown so a macro or spam-clicking cannot flood the wheel. OnSpinWheel checks the permission first, then looks up the last spin time in a Dictionary<ulong, float> keyed by the player's userID and measured with UnityEngine.Time.realtimeSinceStartup. If less than the configured cooldown has passed, it sends the localized cooldown message and returns a non-null object to cancel the spin. Otherwise it records the time and returns null so the wheel spins normally. A one-second repeating timer prunes stale entries so the dictionary does not grow forever, and the timer is destroyed in Unload.
Common use cases
Paid spins through an economy plugin
Check the player's balance with a referenced Economics plugin before allowing a spin, and charge them in the hook. Return non-null when the player cannot pay so no free spin happens.
Cooldown against spin spam
Track the last spin per player and cancel requests that arrive faster than a configured interval, which stops input macros from hammering the RPC.
Disable wheels during a PvP event
Keep a boolean flag set by an admin command and cancel all spins while it is active, so gambling bases cannot be camped mid-event.
Pitfalls
The hook fires after vanilla checks
If the wheel is already spinning fast (velocity above 15) or the player is not authorized, the game never reaches the hook. Do not rely on it to see every spin attempt.
Returning a value is not a reward
A non-null return simply blocks the spin. Rolling prizes yourself means setting velocity or giving items elsewhere; the hook gives you no result to override.
Keep the check cheap
Spin requests can arrive in bursts. Avoid expensive lookups or chat output on every call; store cooldown data in memory and only message the player when the spin is actually blocked.
Build a plugin with OnSpinWheel
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 OnSpinWheel
Generate