Skip to content
RustPluginGenerator

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

HeliEvent.cs
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.

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

Chat and console commands
CommandWhat it doesPermission
/callheliSpawns a patrol helicopter above you and announces it in chat.helievent.call
Permissions
PermissionGrants
helievent.callGrants access to the /callheli chat command.

How the code works

  1. Config and permissions

    The PluginConfig class holds Prefab, SpawnHeight, AutoSpawnEnabled, AutoSpawnMinutes, and AnnounceSpawn. LoadConfig reads it, warns and falls back to defaults on a broken file, and SaveConfig writes it back. Init registers the single permission helievent.call, so /callheli is gated from the start and the timer can be toggled by editing one boolean in oxide/config/HeliEvent.json.

  2. Spawning the helicopter

    SpawnHeli(Vector3 position) calls GameManager.server.CreateEntity(config.Prefab, position, Quaternion.identity) and returns null when the prefab path does not resolve to a BaseHelicopter. After a successful Spawn() the net id is stored in a HashSet<ulong> so OnEntityDeath can 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.

  3. The chat command

    [ChatCommand("callheli")] gives the player a BasePlayer, the command string, and string[] args. The method checks permission.UserHasPermission(player.UserIDString, PermCall), refuses when the player is not connected or building blocked, then spawns at player.transform.position + Vector3.up * config.SpawnHeight. Announcements use Msg so the language file controls wording, and Server.Broadcast sends the spawn message to everyone.

  4. The automatic timer

    In OnServerInitialized the plugin starts timer.Every(config.AutoSpawnMinutes * 60f, ...) only when AutoSpawnEnabled is true and the interval is above zero. The callback spawns one helicopter at world origin raised by SpawnHeight and broadcasts the timer announcement. The Timer instance is stored in a field so Unload can call Destroy() and stop the loop cleanly when the plugin reloads.

  5. Detecting the kill

    OnEntityDeath(BaseCombatEntity entity, HitInfo info) fires for every death, so the first check is entity is BaseHelicopter plus membership in the tracked id set. The killer is resolved from info?.InitiatorPlayer; when that is null the code falls back to info?.Initiator and calls GetOwnerPlayer(), which covers turrets and traps owned by a player. A null killer produces the unknown language entry instead of an exception.

  6. 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. Unload destroys 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

  1. Save HeliEvent.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. 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.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/HeliEvent.json (Carbon: carbon/configs/HeliEvent.json). After editing, reload it:oxide.reload HeliEvent c.reload HeliEvent # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip: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 version
  • Cooldown per player

    Stop one admin or moderator from spamming /callheli by adding a per-player cooldown.

    Generate this version
  • Announce 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.

Prompt: Create a Rust Oxide plugin called HeliEvent with a /callheli permission-gated command that spawns a patrol helicopter at configurable height, optional timed spawns, and a prefab path setting, announcing spawn and destroyer in chat via OnEntityDeath.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize patrol helicopter event

Customize