OnPortalUse
OnPortalUse is called when a player uses a portal, before the portal links to its destination and moves the player. Returning a non-null value cancels the teleport.
object OnPortalUse(BasePlayer player, BasePortal portal)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who is using the portal. Use it to read team, permissions, health, position or to send chat/console feedback. |
portal | BasePortal | The BasePortal entity being entered (the source portal). Its targetPortal and linked state have not been established at this point because the hook fires before LinkPortal runs. |
Return value
Return null (or nothing) to allow the teleport to continue normally. Returning any non-null value stops the original method immediately, so LinkPortal() never runs and the player is not teleported.
How it works
The hook fires inside BasePortal.UsePortal, which runs when a player triggers a portal entity (for example by pressing use on a portal). The call happens at the very top of the method, before LinkPortal() resolves the destination and before any teleport logic runs.
The player argument is the user entering the portal; portal is the source portal entity. Because the method has not linked yet, portal.targetPortal is not yet guaranteed to be set when your hook runs, so treat destination info as unknown at this point.
This is the only injection site, so there are no other entry points to worry about. You cannot narrow calls by type here beyond the declared BasePlayer and BasePortal parameters; declaring a different type would simply skip all calls. Use the hook for gating decisions, not for reading post-teleport state.
Practical uses: blocking portal use during combat, for specific permissions, during events, or while a player carries contraband. Keep the hook body cheap since it runs on every use attempt.
Example plugin: Block portal use while players are in combat
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Portal Combat Block", "RustPluginGenerator", "1.0.0")]
[Description("Blocks portal use for players who recently dealt or took combat damage")]
public class PortalCombatBlock : RustPlugin
{
private const string PermBypass = "portalcombatblock.bypass";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastCombat = new Dictionary<ulong, float>();
private Timer cleanupTimer;
private class PluginConfig
{
[JsonProperty("Combat lockout seconds")]
public float CombatSeconds = 20f;
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
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>
{
["CombatBlocked"] = "You cannot use a portal while in combat."
}, this);
}
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private void OnServerInitialized()
{
cleanupTimer = timer.Every(30f, PruneCombat);
}
private void Unload()
{
cleanupTimer?.Destroy();
}
private void PruneCombat()
{
float now = UnityEngine.Time.realtimeSinceStartup;
var stale = new List<ulong>();
foreach (var entry in lastCombat)
{
if (now - entry.Value > config.CombatSeconds)
stale.Add(entry.Key);
}
foreach (var id in stale)
lastCombat.Remove(id);
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (info == null) return;
var attacker = info.InitiatorPlayer;
if (attacker == null || attacker.IsNpc) return;
var victim = entity as BasePlayer;
if (victim == null || victim.IsNpc) return;
if (attacker == victim) return;
float now = UnityEngine.Time.realtimeSinceStartup;
lastCombat[attacker.userID.Get()] = now;
lastCombat[victim.userID.Get()] = now;
}
private object OnPortalUse(BasePlayer player, BasePortal portal)
{
if (player == null || portal == null) return null;
if (player.IsAdmin || permission.UserHasPermission(player.UserIDString, PermBypass))
return null;
float last;
if (!lastCombat.TryGetValue(player.userID.Get(), out last))
return null;
if (UnityEngine.Time.realtimeSinceStartup - last <= config.CombatSeconds)
{
player.ChatMessage(Msg("CombatBlocked", player.UserIDString));
return true;
}
return null;
}
}
}The plugin records the last time each player dealt or took damage in OnEntityTakeDamage where the damage is between two real players. In OnPortalUse it checks the attacker's last combat timestamp; if the player was in combat within the configured window, it sends a language message and returns a non-null value to cancel the teleport. Config exposes the combat window in seconds, and a timer prunes stale entries every 30 seconds so the dictionary does not grow forever.
Common use cases
Combat lockout
Prevent players from escaping a fight through a portal by blocking use while they recently dealt or took player damage.
Permission-gated portals
Return a non-null value unless the player has a registered permission, making certain portals VIP-only.
Event mode
Toggle a flag so portals are disabled during a server event, then re-enable them afterwards.
Pitfalls
targetPortal not linked yet
The hook fires before
LinkPortal(), so do not rely onportal.targetPortalto know the destination when deciding whether to allow the teleport.Non-null cancels silently
Returning a value aborts the method with no game feedback, so send a chat or console message yourself or players will just see nothing happen.
Runs on every attempt
Keep the handler cheap; heavy reflection, entity scans or spamming console output on each use will hurt performance on busy servers.
Build a plugin with OnPortalUse
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 OnPortalUse
Generate