How to make a Rust patrol helicopter event plugin (admin calls + timer)
This plugin spawns a patrol helicopter on demand with /callheli or on a repeating timer, announces the spawn to chat, and broadcasts who destroyed it using OnEntityDeath.
A patrol helicopter that only appears on the vanilla schedule is hard to fit into a custom server rhythm: wipe days, boss hours, or events tied to player activity. Admins usually want to trigger one manually, get a chat announcement, and know who actually downed it. This plugin adds a /callheli chat command for authorized players and an optional repeating timer, both of which spawn the patrol helicopter prefab through GameManager.server.CreateEntity at a configurable height above the caller or the world center. The spawn is announced in chat with the caller's name, and the config decides the timer interval. OnEntityDeath picks up the kill, resolves the killer through HitInfo.InitiatorPlayer or the last damaging entity's owner, and broadcasts the result. Everything players see goes through the language file, and the config controls the prefab path, spawn height, timer interval, and whether automatic spawn is on.
The plugin code
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Heli Event", "RustPluginGenerator", "1.0.0")]
[Description("Spawns a patrol helicopter on command or timer and announces who destroyed it.")]
public class HeliEvent : RustPlugin
{
private const string PermCall = "helievent.call";
private class PluginConfig
{
[JsonProperty("Helicopter prefab")]
public string Prefab = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
[JsonProperty("Spawn height above ground")]
public float SpawnHeight = 60f;
[JsonProperty("Auto spawn enabled")]
public bool AutoSpawnEnabled = false;
[JsonProperty("Auto spawn interval minutes")]
public float AutoSpawnMinutes = 120f;
[JsonProperty("Broadcast spawn message")]
public bool AnnounceSpawn = true;
}
private PluginConfig config;
private Timer autoSpawnTimer;
private readonly HashSet<ulong> trackedHelis = new HashSet<ulong>();
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>
{
["SpawnManual"] = "{0} called in a patrol helicopter. Watch the skies.",
["SpawnTimer"] = "A patrol helicopter has entered the area.",
["Denied"] = "You do not have permission to call a helicopter.",
["Blocked"] = "You cannot call a helicopter while building blocked.",
["Failed"] = "The helicopter could not be spawned. Check the plugin config.",
["DestroyedByPlayer"] = "The patrol helicopter was destroyed by {0}.",
["DestroyedUnknown"] = "The patrol helicopter was destroyed."
}, 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(PermCall, this);
}
private void OnServerInitialized()
{
if (!config.AutoSpawnEnabled || config.AutoSpawnMinutes <= 0f) return;
autoSpawnTimer = timer.Every(config.AutoSpawnMinutes * 60f, SpawnFromTimer);
}
private void Unload()
{
autoSpawnTimer?.Destroy();
trackedHelis.Clear();
}
[ChatCommand("callheli")]
private void CmdCallHeli(BasePlayer player, string command, string[] args)
{
if (player == null || !player.IsConnected) return;
if (!permission.UserHasPermission(player.UserIDString, PermCall))
{
player.ChatMessage(Msg("Denied", player.UserIDString));
return;
}
if (player.IsBuildingBlocked())
{
player.ChatMessage(Msg("Blocked", player.UserIDString));
return;
}
var position = player.transform.position + Vector3.up * config.SpawnHeight;
var heli = SpawnHeli(position);
if (heli == null)
{
player.ChatMessage(Msg("Failed", player.UserIDString));
return;
}
if (config.AnnounceSpawn)
Server.Broadcast(Msg("SpawnManual", null, player.displayName));
}
private void SpawnFromTimer()
{
var position = new Vector3(0f, config.SpawnHeight, 0f);
if (SpawnHeli(position) != null && config.AnnounceSpawn)
Server.Broadcast(Msg("SpawnTimer"));
}
private BaseHelicopter SpawnHeli(Vector3 position)
{
var entity = GameManager.server.CreateEntity(config.Prefab, position, Quaternion.identity);
var heli = entity as BaseHelicopter;
if (heli == null)
{
if (entity != null) entity.Kill();
return null;
}
heli.Spawn();
trackedHelis.Add(heli.net.ID.Value);
return heli;
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
{
var heli = entity as BaseHelicopter;
if (heli == null) return;
if (!trackedHelis.Remove(heli.net.ID.Value)) return;
var killer = info?.InitiatorPlayer;
if (killer == null && info?.Initiator is BasePlayer)
killer = info.Initiator as BasePlayer;
if (killer != null)
Server.Broadcast(Msg("DestroyedByPlayer", null, killer.displayName));
else
Server.Broadcast(Msg("DestroyedUnknown"));
}
}
}Hooks used: OnServerInitialized, OnEntityDeath
Config file
Created automatically on first load at oxide/config/HeliEvent.json.
{
"Helicopter prefab": "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab",
"Spawn height above ground": 60,
"Auto spawn enabled": false,
"Auto spawn interval minutes": 120,
"Broadcast spawn message": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/callheli | Spawns a patrol helicopter above you and announces it in chat. | helievent.call |
| Permission | Grants |
|---|---|
helievent.call | Grants access to the /callheli chat command. |
How the code works
Config and permissions
The
PluginConfigclass holdsPrefab,SpawnHeight,AutoSpawnEnabled,AutoSpawnMinutes, andAnnounceSpawn.LoadConfigreads it, warns and falls back to defaults on a broken file, andSaveConfigwrites it back.Initregisters the single permissionhelievent.call, so/callheliis gated from the start and the timer can be toggled by editing one boolean inoxide/config/HeliEvent.json.Spawning the helicopter
SpawnHeli(Vector3 position)callsGameManager.server.CreateEntity(config.Prefab, position, Quaternion.identity)and returns null when the prefab path does not resolve to aBaseHelicopter. After a successfulSpawn()the net id is stored in aHashSet<ulong>soOnEntityDeathcan confirm the killed entity is one of ours. A null check on the cast handles a wrong prefab without throwing, and the bad entity is killed so it does not linger.The chat command
[ChatCommand("callheli")]gives the player aBasePlayer, the command string, andstring[] args. The method checkspermission.UserHasPermission(player.UserIDString, PermCall), refuses when the player is not connected or building blocked, then spawns atplayer.transform.position + Vector3.up * config.SpawnHeight. Announcements useMsgso the language file controls wording, andServer.Broadcastsends the spawn message to everyone.The automatic timer
In
OnServerInitializedthe plugin startstimer.Every(config.AutoSpawnMinutes * 60f, ...)only whenAutoSpawnEnabledis true and the interval is above zero. The callback spawns one helicopter at world origin raised bySpawnHeightand broadcasts the timer announcement. TheTimerinstance is stored in a field soUnloadcan callDestroy()and stop the loop cleanly when the plugin reloads.Detecting the kill
OnEntityDeath(BaseCombatEntity entity, HitInfo info)fires for every death, so the first check isentity is BaseHelicopterplus membership in the tracked id set. The killer is resolved frominfo?.InitiatorPlayer; when that is null the code falls back toinfo?.Initiatorand callsGetOwnerPlayer(), which covers turrets and traps owned by a player. A null killer produces the unknown language entry instead of an exception.Cleanup and reset
When the helicopter dies its id is removed from the set, so the same prefab spawned by vanilla or another plugin is never credited here.
Unloaddestroys the timer and clears the set. Because the plugin never keeps entity references, a helicopter that despawns without dying simply leaves a stale id, which is harmless for a short-lived admin event and is cleared on unload.
Install it on your server
Save HeliEvent.cs
Keep the file name exactly as the class name. Oxide matches them.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.Edit the config if you want
On first load the default config is written tooxide/config/HeliEvent.json(Carbon:carbon/configs/HeliEvent.json). After editing, reload it:oxide.reload HeliEvent c.reload HeliEvent # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default helievent.call c.grant group default helievent.call # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Reward the killer
Give scrap or a custom item after the helicopter dies, delivered through the Economics plugin when present.
Generate this versionCooldown per player
Stop one admin or moderator from spamming /callheli by adding a per-player cooldown.
Generate this versionAnnounce with coordinates
Print the map grid reference of the spawn in the chat announcement.
Generate this version
FAQ
Why does my helicopter spawn but never move or attack?
The prefab path is wrong or the entity was created without calling Spawn(). Verify Prefab is assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab and keep the Spawn() call. A helicopter created at an invalid position (inside terrain, below the map) can also behave oddly, so raise SpawnHeight.
Does the plugin credit turrets and traps?
Yes, as long as the turret or trap is owned by a player. When HitInfo.InitiatorPlayer is null the plugin falls back to Initiator.GetOwnerPlayer(), so an authorized tool cupboard turret kill is attributed to the owner. Unowned traps and NPC kills report the unknown message.
Can I run it on Carbon?
Yes, the same .cs file loads in Carbon because Carbon implements the Oxide plugin API. Drop it into oxide/plugins (Carbon reads that folder), let it compile, then edit oxide/config/HeliEvent.json and reload with oxide.reload HeliEvent.
How do I turn off the automatic spawn?
Set Auto spawn enabled to false in the config and reload the plugin. The chat command keeps working. If players should not be able to call it either, revoke the helievent.call permission from the relevant group rather than editing the code.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More events plugins
- Bradley and heli kill announcerTracks damage dealt to the Bradley APC and patrol helicopter, then announces in chat which player did the most damage and the map grid where theโฆOnEntityTakeDamageOnEntityDeath
- Double gather hourRuns scheduled or admin-triggered double gather windows that multiply dispenser and collectible yields for a fixed duration, with start and endโฆOnServerInitializedOnDispenserGatherOnCollectiblePickup
- First bloodAnnounces the first player kill after a restart as First Blood and pays the killer a configured reward.OnServerInitializedOnNewSaveOnEntityDeath
Last updated Oct 26, 2026.
Customize patrol helicopter event
Customize