Skip to content
RustPluginGenerator

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

PveServer.cs
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.

PveServer.json
{
  "Block PvP Damage": true,
  "Protect Owned Entities": true,
  "Allow Team Damage": false,
  "Allow Building Auth": true
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/pvePrints the current PvE rules (PvP blocked on/off, structure protection on/off) and whether you can bypass them.pveserver.use
Permissions
PermissionGrants
pveserver.bypassAllows the player to deal PvP and foreign-structure damage, ignoring the PvE rules (for admins and testing).
pveserver.useAllows use of the /pve chat command.

How the code works

  1. Deciding who is attacking

    Inside OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) the attacker is read from hitInfo.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 returns null and lets Rust handle it untouched. The same happens when attacker and victim are the same object or the attacker holds pveserver.bypass. NPC damage must stay enabled so turrets, scientists and animals keep working.

  2. Blocking player vs player damage

    When entity is a BasePlayer (and not an NPC victim), HandlePlayerDamage runs. If Block PvP Damage is true the hit is nullified and the hook returns 0f, which tells Oxide the damage is handled and nothing is applied. The Allow Team Damage config 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.

  3. Protecting other players' bases

    For non-player victims with Protect Owned Entities enabled, the entity is cast to BaseEntity and passed through IsProtectedEntity, which skips corpses, dropped item containers and supply drops. Structures and deployables carry OwnerID, set at placement. If that owner id equals the attacker's userID.Get(), damage is allowed; otherwise IsAuthorisedOn checks the object's GetBuildingPrivilege() cupboard and IsAuthed. With Allow Building Auth true, a player authed on the cupboard can damage the structure.

  4. 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 with RelationshipManager.ServerInstance.FindTeam and checks whether the entity's OwnerID is in team.members. If it is, the damage proceeds. Set Protect Owned Entities off if you want to disable all foreign-structure protection while keeping pure PvP blocked.

  5. Admin bypass and config

    The pveserver.bypass permission short-circuits both checks before any damage is cancelled. It is registered in Init together with pveserver.use. The [JsonProperty] keys in PluginConfig give admins toggles for PvP, owned entities, team damage and cupboard auth without editing code. LoadConfig falls back to defaults if the file cannot be read and SaveConfig writes the current values; both run automatically on every reload.

  6. 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 from lang, so translations can be added later under oxide/lang/en/PveServer.json. The command requires pveserver.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

  1. Save PveServer.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. 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.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/PveServer.json (Carbon: carbon/configs/PveServer.json). After editing, reload it:oxide.reload PveServer c.reload PveServer # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip: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 version
  • Raid windows for base damage

    Only allow raiding of other players' bases during specified real-world hours while PvP stays blocked.

    Generate this version
  • PvE 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.

Prompt: Create a Rust PvE plugin that blocks player-vs-player damage and player damage to entities owned by other players using OnEntityTakeDamage, with config toggles for PvP, owned entities and team damage, plus a pveserver.bypass permission and /pve command.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize pve server rules

Customize