Skip to content
RustPluginGenerator

OnPhoneDial

OnPhoneDial is called on the PhoneController.CallPhone path after a dialed number resolves to an existing telephone, but before the call state changes to Dialing. Returning a non-null value cancels the dial.

Signature
object OnPhoneDial(PhoneController phoneController, PhoneController telephone, BasePlayer currentPlayer)

Parameters

Hook parameters
ParameterTypeDescription
phoneControllerPhoneControllerThe PhoneController of the player who is placing the call. Compare it against telephone to detect self-dials or key off its owner.
telephonePhoneControllerThe targeted PhoneController resolved from the dialed number, or null if no telephone with that number exists. Only a non-null value reaches the hook.
currentPlayerBasePlayerThe BasePlayer initiating the call. Use it for permission checks, distance checks, team comparisons and to send chat feedback.

Return value

Returning null (or having no hook) lets the dial continue into the normal idle and receive checks. Returning any non-null value (including false) aborts the original CallPhone path immediately: no Dialing state is set and the last dialed number is not stored.

How it works

The hook fires when a player submits a number on a telephone and the game has already resolved that number to a PhoneController (TelephoneManager.GetTelephone). It runs before the target's idle and receive state is validated and before SetPhoneState(Telephone.CallState.Dialing) and lastDialedNumber are written, so it is the correct place to approve or reject a call. The first argument is the caller's controller, the second is the resolved target, and currentPlayer is the player who dialed. Because the target is resolved before the hook, telephone is never null when the hook runs; a dial of an unknown number follows a different path and never reaches it. Declaring telephone or phoneController as a narrower type is of little use here since the resolved type is always PhoneController. Note that blocking here does not send any message to the caller - the game simply returns, so tell the player yourself. The hook also fires only on the initiating side; the receiving phone's behavior is governed by the answer and call-start path.

Example plugin: Restrict phone calls to teammates with a permission

PhoneTeamDial.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Phone Team Dial", "RustPluginGenerator", "1.0.0")]
    [Description("Restricts telephone dials to teammates and optionally requires a permission")]
    public class PhoneTeamDial : RustPlugin
    {
        private const string PermUse = "phoneteamdial.use";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Require permission to dial")]
            public bool RequirePermission { get; set; } = true;

            [JsonProperty("Block calls outside the caller's team")]
            public bool TeamOnly { 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 use the telephone network.",
                ["NotTeam"] = "You can only call members of your own team."
            }, this);
        }

        private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        private object OnPhoneDial(PhoneController phoneController, PhoneController telephone, BasePlayer currentPlayer)
        {
            if (currentPlayer == null || telephone == null) return null;

            if (config.RequirePermission && !permission.UserHasPermission(currentPlayer.UserIDString, PermUse))
            {
                currentPlayer.ChatMessage(Msg("NoPermission", currentPlayer.UserIDString));
                return false;
            }

            if (!config.TeamOnly) return null;

            BaseEntity entity = telephone.baseEntity;
            if (entity == null) return null;

            BasePlayer target = BasePlayer.FindByID(entity.OwnerID);
            if (target == null || target.userID == currentPlayer.userID) return null;

            if (IsSameTeam(currentPlayer, target)) return null;

            currentPlayer.ChatMessage(Msg("NotTeam", currentPlayer.UserIDString));
            return false;
        }

        private bool IsSameTeam(BasePlayer player, BasePlayer other)
        {
            if (player.currentTeam == 0 || player.currentTeam != other.currentTeam) return false;

            var team = RelationshipManager.ServerInstance?.FindTeam(player.currentTeam);
            return team != null && team.members != null && team.members.Contains(other.userID);
        }
    }
}

The plugin reads two config values: RequirePermission controls whether players need phoneteamdial.use to dial at all, and TeamOnly controls whether calls outside the caller's team are blocked. OnPhoneDial resolves the target's owner with BasePlayer.FindByID(telephone.baseEntity.OwnerID) and compares it against the caller. Same-player dials are always allowed so players can test their own phones. Team membership is checked through RelationshipManager.ServerInstance?.FindTeam; team id 0, a missing team, or a missing members list all count as not being on the same team. Blocked dials return false to stop the call and send a localized chat message explaining why, while allowed dials return null. Permissions are checked against currentPlayer.UserIDString.

Common use cases

  • Team-only telephone network

    Force calls to stay inside a team so cross-map call spam and information leaks through random number dialing are impossible.

  • Paid or permission-gated calling

    Check a permission such as phonevip.call before allowing dials, and point players to a store or command that grants it.

  • Call cooldown and anti-spam

    Track a per-player timestamp in a dictionary and reject dials that arrive too soon, stopping rapid redial scripts from occupying lines.

Pitfalls

  • The hook does not run for unknown numbers

    Numbers without a registered telephone never reach OnPhoneDial, so you cannot use it to block or log failed lookups. Use the dial-fail hooks for that side.

  • No feedback is sent on cancel

    Returning a value only stops the dial path. Without your own ChatMessage or SendReply the caller sees nothing happen and may assume the plugin is broken.

  • getOwnerPlayer can be null

    Entities without a parent player return null, so always null-check the resolved owner before comparing UserIDs or team ids.

Build a plugin with OnPhoneDial

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Team-only telephone network using the OnPhoneDial hook, with a config file and a permission
Generate this plugin

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 OnPhoneDial

Generate