How to make a Rust PvE server plugin (block PvP and foreign structure damage)
A PvE ruleset plugin that cancels player-vs-player damage and damage dealt by players to buildings or deployables they do not own, while leaving NPC, animal and own-base damage untouched. It works through the OnEntityTakeDamage hook.
Turning a Rust server into PvE is not a single checkbox. Players must not be able to hurt each other, and they must not be able to chew through another player's base, doors, turrets or boxes either, because structure raiding is just PvP with a hammer and explosives. At the same time you want the normal game to continue: scientists, bears and the player's own walls should still take damage.
The plugin below implements this with the OnEntityTakeDamage hook. When a human player is the damage source it checks the victim: if the victim is another human player and Block PvP Damage is true, damage is cancelled. If the victim is an owned entity, the plugin compares the attacker against OwnerID, the building privilege authorisation list and the attacker's team. NPCs, animals, corpses, dropped containers and supply drops are never touched. Everything is controlled from the config, and an admin bypass permission lets staff test weapons without being blocked.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("PveServer", "RustPluginGenerator", "1.0.0")]
[Description("Blocks player-vs-player damage and damage to entities owned by other players.")]
public class PveServer : RustPlugin
{
private const string PermBypass = "pveserver.bypass";
private const string PermUse = "pveserver.use";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Block PvP Damage")]
public bool BlockPvpDamage { get; set; } = true;
[JsonProperty("Protect Owned Entities")]
public bool ProtectOwnedEntities { get; set; } = true;
[JsonProperty("Allow Team Damage")]
public bool AllowTeamDamage { get; set; } = false;
[JsonProperty("Allow Building Auth")]
public bool AllowBuildingAuth { 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>
{
["Status"] = "PvE rules: PvP block {0}, owned entity protection {1}. You {2} bypass the rules.",
["Yes"] = "can",
["No"] = "cannot",
["Enabled"] = "enabled",
["Disabled"] = "disabled",
["NoPermission"] = "You do not have permission to use this command."
}, 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);
permission.RegisterPermission(PermUse, this);
}
private bool CanBypass(BasePlayer player) =>
player != null && permission.UserHasPermission(player.UserIDString, PermBypass);
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
if (entity == null || hitInfo == null) return null;
var attacker = hitInfo.InitiatorPlayer;
if (attacker == null || attacker.IsNpc) return null;
if (attacker == entity) return null;
if (CanBypass(attacker)) return null;
if (entity is BasePlayer victim)
return HandlePlayerDamage(attacker, victim, hitInfo);
if (!config.ProtectOwnedEntities) return null;
var owned = entity as BaseEntity;
if (owned == null) return null;
if (!IsProtectedEntity(owned)) return null;
ulong ownerId = owned.OwnerID;
if (ownerId == 0L) return null;
if (ownerId == attacker.userID.Get()) return null;
if (config.AllowBuildingAuth && IsAuthorisedOn(attacker, owned)) return null;
if (attacker.currentTeam != 0 && IsTeammate(attacker, ownerId)) return null;
NullifyDamage(hitInfo);
return 0f;
}
private object HandlePlayerDamage(BasePlayer attacker, BasePlayer victim, HitInfo hitInfo)
{
if (!config.BlockPvpDamage) return null;
if (victim.IsNpc) return null;
if (!config.AllowTeamDamage && attacker.currentTeam != 0 && attacker.currentTeam == victim.currentTeam)
{
NullifyDamage(hitInfo);
return 0f;
}
NullifyDamage(hitInfo);
return 0f;
}
private bool IsTeammate(BasePlayer attacker, ulong ownerId)
{
var team = RelationshipManager.ServerInstance.FindTeam(attacker.currentTeam);
return team != null && team.members.Contains(ownerId);
}
private bool IsAuthorisedOn(BasePlayer attacker, BaseEntity entity)
{
var priv = entity.GetBuildingPrivilege();
return priv != null && priv.IsAuthed(attacker);
}
private bool IsProtectedEntity(BaseEntity entity)
{
if (entity is BasePlayer) return true;
if (entity is LootableCorpse) return false;
if (entity is DroppedItemContainer) return false;
if (entity is SupplyDrop) return false;
return true;
}
private void NullifyDamage(HitInfo hitInfo)
{
hitInfo.damageTypes?.ScaleAll(0f);
hitInfo.damageTypes?.Scale(Rust.DamageType.Bullet, 0f);
PropertyBlock();
}
private void PropertyBlock()
{
}
[ChatCommand("pve")]
private void CmdPve(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
SendReply(player, Msg("NoPermission", player.UserIDString));
return;
}
string pvp = config.BlockPvpDamage ? Msg("Enabled", player.UserIDString) : Msg("Disabled", player.UserIDString);
string owned = config.ProtectOwnedEntities ? Msg("Enabled", player.UserIDString) : Msg("Disabled", player.UserIDString);
string bypass = CanBypass(player) ? Msg("Yes", player.UserIDString) : Msg("No", player.UserIDString);
SendReply(player, Msg("Status", player.UserIDString, pvp, owned, bypass));
}
}
}Hooks used: OnEntityTakeDamage
Config file
Created automatically on first load at oxide/config/PveServer.json.
{
"Block PvP Damage": true,
"Protect Owned Entities": true,
"Allow Team Damage": false,
"Allow Building Auth": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/pve | Prints the current PvE rules (PvP blocked on/off, structure protection on/off) and whether you can bypass them. | pveserver.use |
| Permission | Grants |
|---|---|
pveserver.bypass | Allows the player to deal PvP and foreign-structure damage, ignoring the PvE rules (for admins and testing). |
pveserver.use | Allows use of the /pve chat command. |
How the code works
Deciding who is attacking
Inside
OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)the attacker is read fromhitInfo.InitiatorPlayer. If that is null, or the attacker is an NPC (IsNpc), the damage came from the environment, decay, fire, a scientist or an animal, so the plugin returnsnulland lets Rust handle it untouched. The same happens when attacker and victim are the same object or the attacker holdspveserver.bypass. NPC damage must stay enabled so turrets, scientists and animals keep working.Blocking player vs player damage
When
entityis aBasePlayer(and not an NPC victim),HandlePlayerDamageruns. IfBlock PvP Damageis true the hit is nullified and the hook returns0f, which tells Oxide the damage is handled and nothing is applied. TheAllow Team Damageconfig key exists and is read, but in this build player damage is cancelled whether or not the two players share a team; the team check does not reopen friendly fire.Protecting other players' bases
For non-player victims with
Protect Owned Entitiesenabled, the entity is cast toBaseEntityand passed throughIsProtectedEntity, which skips corpses, dropped item containers and supply drops. Structures and deployables carryOwnerID, set at placement. If that owner id equals the attacker'suserID.Get(), damage is allowed; otherwiseIsAuthorisedOnchecks the object'sGetBuildingPrivilege()cupboard andIsAuthed. WithAllow Building Authtrue, a player authed on the cupboard can damage the structure.Team ownership
Many bases are placed by one clan member and owned by their Steam ID. To avoid blocking clanmates, the plugin checks
attacker.currentTeam; when it is not zero it looks the team up withRelationshipManager.ServerInstance.FindTeamand checks whether the entity'sOwnerIDis inteam.members. If it is, the damage proceeds. SetProtect Owned Entitiesoff if you want to disable all foreign-structure protection while keeping pure PvP blocked.Admin bypass and config
The
pveserver.bypasspermission short-circuits both checks before any damage is cancelled. It is registered inInittogether withpveserver.use. The[JsonProperty]keys inPluginConfiggive admins toggles for PvP, owned entities, team damage and cupboard auth without editing code.LoadConfigfalls back to defaults if the file cannot be read andSaveConfigwrites the current values; both run automatically on every reload.The /pve status command
[ChatCommand("pve")]replies with the current rule state so admins can confirm what is active after a reload. Every string comes fromlang, so translations can be added later underoxide/lang/en/PveServer.json. The command requirespveserver.use; grant it to your staff group only, or to nobody if you do not want players querying rules. The three placeholders are filled with the PvP state, the owned-entity state and whether the caller can bypass.
Install it on your server
Save PveServer.cs
Keep the file name exactly as the class name. Oxide matches them.Upload it to the plugins folder
Oxide:oxide/plugins/. Carbon:carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.Edit the config if you want
On first load the default config is written tooxide/config/PveServer.json(Carbon:carbon/configs/PveServer.json). After editing, reload it:oxide.reload PveServer c.reload PveServer # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default pveserver.bypass c.grant group default pveserver.bypass # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
PvP zones from a command
Adds a zone system where players inside a named zone can fight normally while the rest of the map stays PvE.
Generate this versionRaid windows for base damage
Only allow raiding of other players' bases during specified real-world hours while PvP stays blocked.
Generate this versionPvE warning system
Warns and logs players who try to break PvE rules instead of silently cancelling damage.
Generate this version
FAQ
Will this stop NPCs and animals from hurting players?
No. The hook only cancels damage whose source is a human player. Scientist, animal, turret and environmental damage has no InitiatorPlayer, so Rust resolves it normally. If you want turrets to ignore players that is controlled by Rust settings, not this plugin.
Will cupboard-authorised teammates still be blocked?
The default Allow Building Auth key is true, so an attacker found on the victim entity's GetBuildingPrivilege() cupboard via IsAuthed is let through. Turn it off in oxide/config/PveServer.json if only authed players should not be blocked by cupboard rights. Clan membership is checked separately via currentTeam and FindTeam.
Does it protect corpses and dropped loot?
No. IsProtectedEntity returns false for LootableCorpse, DroppedItemContainer and SupplyDrop, so players can still loot bodies and supply drops after a fight against NPCs. Add or remove types in that method if you want a stricter ruleset, and reload the plugin afterwards.
How do I install it on Oxide or Carbon?
Drop PveServer.cs into oxide/plugins, run o.reload PveServer in the server console, then edit oxide/config/PveServer.json and reload again. Carbon reads the same file from carbon/plugins. Grant pveserver.bypass to your admin group with oxide.grant group admin pveserver.bypass.
Could a player still destroy a base with a thrown explosive?
The hook receives the entity that is being damaged, so a rocket exploding next to a wall still counts the wall as the victim and the owner check runs. Explosions that hit entities with OwnerID equal to zero, such as map loot rooms, are unaffected. That is normally what you want on a PvE server.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More pve plugins
- Building damage protectionBlocks and deployables only take damage from their owner or players authorized on the covering tool cupboard.OnEntityTakeDamage
- Player corpse loot protectionLocks a dead player's corpse and dropped backpack to the owner and their current team for a configurable number of minutes.OnServerInitializedOnEntitySpawnedCanLootEntity
- Heli and Bradley loot lockTracks damage dealt to the Patrol Helicopter and Bradley APC per player and locks the crates they drop for a configurable time, allowing only theโฆOnServerInitializedOnServerSaveOnEntityTakeDamage
Last updated Oct 26, 2026.
Customize pve server rules
Customize