CanSetRelationship
CanSetRelationship is called from RelationshipManager.SetRelationship before the server writes a relationship entry between two players. Returning a non-null value cancels the write so the existing relationship stays as it was.
object CanSetRelationship(BasePlayer player, BasePlayer otherPlayer, RelationshipManager.RelationshipType type, int weight)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The player whose relationship entry is being written. Never null when the hook is reached, because the game already returned for null players. |
otherPlayer | BasePlayer | The second player in the relationship pair. Can be null in the original guard, so validate it before reading any member. |
type | RelationshipManager.RelationshipType | The RelationshipManager.RelationshipType being assigned (for example Ally, Enemy or Neutral); read it to allow or deny per relationship kind. |
weight | int | The integer weight the game wants to store for this relationship entry; typical values are 0, 1 or higher depending on the caller. |
Return value
Return null to let the relationship be written as the game intended. Return any non-null object (a boolean or a string are the usual choices) to stop the write; the method returns immediately and the existing relationship data is left untouched.
How it works
The hook fires inside RelationshipManager.SetRelationship, before Oxide touches the relationship storage. Every internal game call that sets a player-to-player relationship goes through this method, so the hook sees reputation changes, ally/enemy assignments and any other code path that reuses it. At the injection point the game has already filtered out null players, self-pairs and NPCs; otherPlayer can still be null when the check runs, so guard it. player is the owner of the entry, type is the new relationship kind and weight is the integer the server intends to store. Returning anything non-null aborts the whole write, which means the players keep their previous relationship values for both sides. Because the hook fires on every relationship write, keep the body cheap and never print to console there. Declaring BasePlayer for both player parameters matches the signature; narrowing them further is not useful since they are always Player-type objects.
Example plugin: Block relationship changes between non-teammates
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Relationship Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks relationship writes between players who are not on the same team")]
public class RelationshipGuard : RustPlugin
{
private const string PermBypass = "relationshipguard.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Block enemy relationships")]
public bool BlockEnemy { get; set; } = true;
[JsonProperty("Block ally relationships outside teams")]
public bool BlockAlly { get; set; } = false;
[JsonProperty("Allow admin permission bypass")]
public bool AllowAdminBypass { 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>
{
["Blocked"] = "Relationship change between {0} and {1} blocked ({2})."
}, 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);
}
private object CanSetRelationship(BasePlayer player, BasePlayer otherPlayer, RelationshipManager.RelationshipType type, int weight)
{
if (player == null || otherPlayer == null) return null;
if (player.IsNpc || otherPlayer.IsNpc) return null;
if (config.AllowAdminBypass)
{
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
if (permission.UserHasPermission(otherPlayer.UserIDString, PermBypass)) return null;
}
bool sameTeam = player.currentTeam != 0 && player.currentTeam == otherPlayer.currentTeam;
if (sameTeam) return null;
if (type.ToString() == "Enemy" && config.BlockEnemy) return true;
if (type.ToString() == "Ally" && config.BlockAlly) return true;
return null;
}
}
}The plugin prevents the server from writing Ally or Enemy relationship entries between two players who are not on the same team. Its CanSetRelationship hook declares both player parameters as BasePlayer and also requires a RelationshipManager.RelationshipType type and an int weight, so Oxide only calls it when those arguments match. Inside, the plugin checks the Block enemy relationships and Block ally relationships outside teams config flags to decide which relationship types to block. Same-team pairs are always allowed through. If the pair is not on the same team and the relationship type is blocked, the hook returns true, which cancels the game's write and leaves both players' relationships unchanged. Otherwise it returns null and normal behavior continues. The config flag Allow admin permission bypass controls whether either player holding the relationshipguard.bypass permission skips the block.
Common use cases
Keep reputation and ally state stable on PvE servers
Return a non-null value for Enemy relationships so the game cannot silently mark players hostile when they shoot in a safe zone.
Enforce team-only relationships
Allow Ally writes only when
player.currentTeamis non-zero and matchesotherPlayer.currentTeam, and block everything else.Log-only audit for admins
Store a rolling list of relationship writes in memory and expose a console command so staff can inspect which plugin or event changed a player's reputation.
Pitfalls
otherPlayer can be null
The original guard checks
otherPlayer != nullonly when reading IsNpc, so the hook can be reached with a null second argument. Always null-check before touching it.Fires on every relationship write
Reputation recalculation and ally assignment hit this method frequently. Keep the body to a few field reads and never call PrintWarning or Puts inside it.
Cancelling blocks both sides
Returning a value stops SetRelationship entirely, so the opposite direction of the pair is not updated either. If you only want to deny one direction, check
playerandotherPlayercarefully.
Build a plugin with CanSetRelationship
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 CanSetRelationship
Generate