OnPhoneAnswered
OnPhoneAnswered is called after a phone call has been successfully answered and both PhoneControllers have started their call state. It fires only when the answer was not cancelled by OnPhoneAnswer.
void OnPhoneAnswered(PhoneController phoneController, PhoneController activeCallTo)Parameters
| Parameter | Type | Description |
|---|---|---|
phoneController | PhoneController | The PhoneController of the player who picked up the call. Use its OwnerID or its position to identify the player and the phone they answered. |
activeCallTo | PhoneController | The PhoneController of the caller whose line is now connected. Its OwnerID identifies the player on the other end of the call. |
Return value
The return value is ignored. The hook fires after the call state has already been established on both phones, so returning a value cannot cancel or alter the answer.
How it works
The hook fires at the end of PhoneController.AnswerPhone, after UpdateServerPlayer, BeginCall() on both controllers and the preceding OnPhoneAnswer check have all run. In other words, it only fires for calls that were actually established: if another plugin blocks the answer by returning a non-null value from OnPhoneAnswer, this hook is never reached.
Both parameters are PhoneController components. phoneController is the handset that was picked up (the answering side), and activeCallTo is the remote handset that is now connected to it. Read OwnerID on either to map the controller to a player, or transform.position to get the physical location of the phone.
This hook does not fire for unanswered calls, busy signals, or when the dial side times out. It is also not a per-ring event; it fires once per successful answer.
Declaring the parameters as the exact PhoneController type is required, since broader types will make Oxide skip the call, and there is nothing in the dispatch to differentiate the two arguments. Keep the handler cheap because it runs inline with the answer path.
Example plugin: Notify players in chat when a call is answered
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Phone Call Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Announces answered phone calls in chat and notifies both participants.")]
public class PhoneCallNotifier : RustPlugin
{
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Announce answered calls to all players")]
public bool AnnounceToServer { get; set; } = true;
[JsonProperty("Message other party display name")]
public bool UseDisplayName { 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>
{
["Connected"] = "You are now connected to {0}.",
["ServerAnnounce"] = "{0} and {1} connected a phone call.",
["Unknown"] = "an unknown caller"
}, this);
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private void OnPhoneAnswered(PhoneController phoneController, PhoneController activeCallTo)
{
if (phoneController == null || activeCallTo == null) return;
BaseEntity answererEntity = phoneController.baseEntity;
BaseEntity callerEntity = activeCallTo.baseEntity;
BasePlayer answerer = ResolveOwner(answererEntity, null);
BasePlayer caller = ResolveOwner(callerEntity, null);
string answererName = answerer != null ? answerer.displayName : Msg("Unknown");
string callerName = caller != null ? caller.displayName : Msg("Unknown");
if (answerer != null && answerer.IsConnected)
{
answerer.ChatMessage(Msg("Connected", answerer.UserIDString, config.UseDisplayName ? callerName : "the other party"));
}
if (caller != null && caller.IsConnected)
{
caller.ChatMessage(Msg("Connected", caller.UserIDString, config.UseDisplayName ? answererName : "the other party"));
}
if (config.AnnounceToServer)
{
Server.Broadcast(Msg("ServerAnnounce", null, callerName, answererName));
}
}
private BasePlayer ResolveOwner(BaseEntity entity, BasePlayer fallback)
{
if (fallback != null && fallback.IsConnected) return fallback;
if (entity == null) return null;
ulong id = entity.OwnerID;
if (id < 76560000000000000UL) return null;
return BasePlayer.FindByID(id);
}
}
}The plugin listens for OnPhoneAnswered and resolves both PhoneController instances to BasePlayer objects through OwnerID and BasePlayer.FindByID. Each connected participant receives a localized chat message naming the other party when the UseDisplayName config option is enabled; otherwise the message says "the other party". If the Announce answered calls to all players config option is enabled, a localized broadcast tells the server that a call was connected, which fits public servers that want call activity in the chat feed. All text goes through the language file, and the plugin keeps no state, timers, or data files, so unloading is trivial.
Common use cases
Call activity feed
Broadcast a short message to the server whenever two players connect a phone call, giving admins an at-a-glance log of in-game communication.
Per-player call counters
Increment a counter for each participant using a data file so you can later reward frequent callers or display a stat in a UI panel.
Restricted phone zones
Check
phoneController.transform.positionagainst a list of zone coordinates and warn or teleport players who answer a call inside a no-phone area.
Pitfalls
Does not fire when OnPhoneAnswer blocks
If another plugin returns a non-null value from
OnPhoneAnswer, the connection is aborted and this hook is skipped entirely. Do not rely on it for cleanup of blocked calls.OwnerID may not resolve to a player
Phone controllers persist as world entities, so
OwnerIDcan point at a player who has since disconnected or never set ownership. Always null-check theBasePlayer.FindByIDresult before sending messages.Fires only on success
Missed calls, timeouts, and the busy state never reach this hook. Use
OnPhoneDialFailedorOnPhoneDialTimeoutfor the failure side of the flow.
Build a plugin with OnPhoneAnswered
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 OnPhoneAnswered
Generate