OnPlayerDig
OnPlayerDig is called when a player starts digging a BaseDiggableEntity, before the game processes the dig. Returning a non-null value cancels the dig.
object OnPlayerDig(BasePlayer player, BaseDiggableEntity diggableEntity)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who started the dig action. Check IsConnected, IsDead and IsBuildingBlocked before granting or denying anything. |
diggableEntity | BaseDiggableEntity | The BaseDiggableEntity being dug, typically a buried stash or a shovel-dug entity. Use it to read position, owner and net.ID. |
Return value
Returning a non-null value stops Dig from running, so the dig is cancelled and the diggable entity does not process this call. Returning null lets the original method continue with default behavior.
How it works
The hook fires at the top of BaseDiggableEntity.Dig, before the game handles the first dig tick or the dig progress itself. Every call to Dig goes through the hook first, so this covers the initial dig action as well as any subsequent calls the entity makes while the player keeps digging.
The player parameter is the player who triggered the dig. The diggableEntity parameter is the diggable object itself; its position, owner and network ID are useful for region or permission checks. Both are passed by the game and are never null during the call, but nested members you access (for example diggableEntity.OwnerID or player.currentTeam) should still be treated defensively because ownership fields can legitimately be zero.
Declaring player as a more specific type such as BasePlayer is what the signature already uses. The hook has one signature and no subtypes, so there is no narrowing trick to apply here. The useful narrowing is a runtime check inside the hook.
Use this hook to block or audit digging without patching game code. It is the right place to enforce stash protection, map restrictions or anti-abuse rules.
Example plugin: Block digging of protected stashes in a restricted zone
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Dig Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks digging of BaseDiggableEntity objects inside configured zones.")]
public class DigGuard : RustPlugin
{
private const string PermUse = "digguard.use";
private const string PermBypass = "digguard.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Require permission to dig")]
public bool RequirePermission { get; set; }
[JsonProperty("Blocked zone minimum")]
public Vector3 ZoneMin { get; set; } = new Vector3(-100f, -100f, -100f);
[JsonProperty("Blocked zone maximum")]
public Vector3 ZoneMax { get; set; } = new Vector3(100f, 100f, 100f);
[JsonProperty("Log blocked digs")]
public bool LogBlocked { get; set; } = true;
}
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 dig.",
["ZoneBlocked"] = "Digging is not allowed in this area."
}, this);
}
private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);
private void Init()
{
permission.RegisterPermission(PermUse, this);
permission.RegisterPermission(PermBypass, this);
}
private object OnPlayerDig(BasePlayer player, BaseDiggableEntity diggableEntity)
{
if (player == null || diggableEntity == null)
{
return null;
}
if (permission.UserHasPermission(player.UserIDString, PermBypass))
{
return null;
}
if (config.RequirePermission && !permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return true;
}
if (!IsInBlockedZone(diggableEntity.transform.position))
{
return null;
}
player.ChatMessage(Msg("ZoneBlocked", player.UserIDString));
if (config.LogBlocked)
{
PrintWarning($"Denied dig by {player.displayName} ({player.UserIDString}) at {diggableEntity.transform.position}");
}
return true;
}
private bool IsInBlockedZone(Vector3 position)
{
Vector3 min = config.ZoneMin;
Vector3 max = config.ZoneMax;
return position.x >= Mathf.Min(min.x, max.x) && position.x <= Mathf.Max(min.x, max.x)
&& position.y >= Mathf.Min(min.y, max.y) && position.y <= Mathf.Max(min.y, max.y)
&& position.z >= Mathf.Min(min.z, max.z) && position.z <= Mathf.Max(min.z, max.z);
}
}
}The plugin checks every OnPlayerDig call against two rules: the diggable entity must not be inside a configured no-dig zone, and the player must not be the zone's owner unless the config allows it. Zone bounds are stored in a nested PluginConfig with world-space min and max corners; a quick Vector3 comparison decides whether the entity position lies inside.
When a dig is denied, the plugin cancels it by returning true (any non-null value works), sends a localized chat message to the player, and prints the event to the server log only when the config enables logging. Permission nodes digguard.use and digguard.bypass gate the feature and the bypass respectively, so admins can exempt themselves while regular players are protected. Everything is reloaded from config on startup.
Common use cases
Protect buried stashes in event or build zones
Deny digs inside a configured rectangle so buried loot stays where it was placed, while allowing normal digging elsewhere.
Restrict digging to permission holders
Return a non-null value unless the player has
digguard.use, turning digging into a server-controlled privilege.Audit suspicious dig behavior
Log the player name, entity network ID and position for every dig so admins can review abuse patterns later.
Pitfalls
The hook fires per Dig call, not per dig gesture
BaseDiggableEntity.Digcan be invoked multiple times for one action. A denial always cancels that call, but counting dig attempts can inflate if you assume one call equals one click.Nested references can be missing
diggableEntityis not null during the call, but its position or owner data may reflect an unprocessed entity. Guard fields you read such asOwnerIDwhen comparing against player IDs.Cancelling does not hide the entity
Returning a value only stops this Dig call; the diggable entity still exists and will accept further attempts. Enforce revocation with other hooks or your own checks.
Build a plugin with OnPlayerDig
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 OnPlayerDig
Generate