Skip to content
RustPluginGenerator

OnCargoPlaneSignaled

OnCargoPlaneSignaled is called when a thrown Supply Signal spawns its cargo plane, right after the plane entity is created and spawned but before the signal's timer finishes.

Signature
void OnCargoPlaneSignaled(BaseEntity entity, SupplySignal supplySignal)

Parameters

Hook parameters
ParameterTypeDescription
entityBaseEntityThe cargo plane entity that was just created and spawned by the signal. It is a BaseEntity with net identity, so you can read its net.ID, transform.position, ShortPrefabName, and call Kill() or SendNetworkUpdate().
supplySignalSupplySignalThe SupplySignal entity that was thrown and is now exploding. Use it to read transform.position, OwnerID, or the item skin if you need to identify who called the drop or customize the response.

Return value

The return value is ignored; the game continues its normal path regardless of what you return. Return nothing (void hook methods simply end) and never rely on the hook to cancel the plane.

How it works

Oxide fires this hook inside SupplySignal.Explode, immediately after the game creates the cargo plane entity, sets its drop position, and calls Spawn() on it. This means the plane already exists in the world and has a valid network identity when your hook runs, but the Invoke(FinishUp, 210f) cleanup and the On flag update happen after the hook returns.

The entity parameter is the freshly spawned cargo plane. You can still cancel or redirect it here because nothing has been sent to clients yet in a way you cannot undo — calling Kill() removes it, and you can read net.ID to track it later. The supplySignal parameter is the thrown signal entity, useful for position, owner, or skin-based routing.

Declaring a more specific type narrows dispatch: if you write BaseEntity the hook always fires; if you declare CargoPlane Oxide skips calls where the spawned entity is somehow not a cargo plane (rare, but possible with prefab swaps). The signal can be typed as SupplySignal or ThrownEntity depending on what you need.

There is no return value handling — this is a notification hook.

Example plugin: Rewrite the cargo plane drop position to a random offset

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

namespace Oxide.Plugins
{
    [Info("Cargo Plane Randomizer", "RustPluginGenerator", "1.0.0")]
    [Description("Shifts cargo plane drop positions by a random offset when a supply signal fires.")]
    public class CargoPlaneRandomizer : RustPlugin
    {
        private const string PermAdmin = "cargoPlanerandomizer.admin";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Enable random offset")]
            public bool Enabled { get; set; } = true;

            [JsonProperty("Maximum random offset (meters)")]
            public float MaxOffset { get; set; } = 60f;

            [JsonProperty("Broadcast plane announcement")]
            public bool Announce { 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>
            {
                ["Incoming"] = "Cargo plane inbound, dropping near {0}",
                ["ToggleOn"] = "Cargo plane offset enabled.",
                ["ToggleOff"] = "Cargo plane offset disabled."
            }, 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(PermAdmin, this);
        }

        private void OnCargoPlaneSignaled(BaseEntity entity, SupplySignal supplySignal)
        {
            if (entity == null || entity.IsDestroyed) return;

            if (config.Enabled)
            {
                float max = Mathf.Clamp(config.MaxOffset, 0f, 500f);
                if (max > 0.5f)
                {
                    Vector3 pos = entity.transform.position;
                    Vector3 offset = new Vector3(
                        UnityEngine.Random.Range(-max, max),
                        0f,
                        UnityEngine.Random.Range(-max, max));
                    entity.SendMessage("InitDropPosition", pos + offset, SendMessageOptions.DontRequireReceiver);
                    entity.SendNetworkUpdate();
                }
            }

            if (config.Announce)
            {
                string direction = CompassDirection(entity.transform.position);
                Server.Broadcast(Msg("Incoming", null, direction));
            }
        }

        private string CompassDirection(Vector3 pos)
        {
            float angle = Mathf.Atan2(pos.x, pos.z) * Mathf.Rad2Deg;
            string[] dirs = { "north", "north-east", "east", "south-east", "south", "south-west", "west", "north-west" };
            int index = Mathf.RoundToInt(Mathf.Repeat(angle / 45f, 8));
            return dirs[index];
        }

        [ConsoleCommand("cargoplane.toggle")]
        private void CmdToggle(ConsoleSystem.Arg arg)
        {
            if (arg.Player() != null && !permission.UserHasPermission(arg.Player().UserIDString, PermAdmin))
            {
                arg.ReplyWith("You do not have permission.");
                return;
            }

            config.Enabled = !config.Enabled;
            SaveConfig();
            arg.ReplyWith(Msg(config.Enabled ? "ToggleOn" : "ToggleOff"));
        }
    }
}

The plugin listens for OnCargoPlaneSignaled and checks the config for an enabled flag and a maximum offset. If the plane entity is valid and the offset is above zero, it reads the current plane position, adds a random horizontal offset within the configured radius, and sends an InitDropPosition message with DontRequireReceiver — the same message the game itself uses — so the plane drifts to the new drop point instead of the original.

It also broadcasts an optional chat notice to all players using the language file. Config controls whether the message is sent and the maximum radius; Set to 0 to disable the shuffle. A [ConsoleCommand] lets admins toggle it at runtime.

Common use cases

  • Announce incoming cargo planes to the whole server

    Use supplySignal.transform.position and entity.net.ID to broadcast a chat message with a compass direction so players know where to look. Combine with the plane's lifetime to remind them of the 210 second window.

  • Track supply signals per player for stats

    Read supplySignal.OwnerID (or the item's owner via the signal entity) and increment a per-player counter in oxide/data. Useful for leaderboards or anti-abuse cooldowns on signal usage.

  • Redistribute cargo drops away from monuments

    Sample a random position, check a configured list of excluded zones, and send InitDropPosition to the plane to move it before players catch the visual. Keeps loot away from heavily camped areas.

Pitfalls

  • Do not expect to cancel the drop

    The hook is a notification — there is no return value that stops the game code. If you need to cancel, call entity.Kill() inside the hook, but note the signal entity itself still explodes and despawns on its normal timer.

  • The plane is already spawned

    By the time your hook runs, Spawn() has been called and the entity has a net ID. Any change you make (Kill, InitDropPosition message, SetFlag) is sent after the initial spawn, so use SendNetworkUpdate() if clients might have already received the first update.

  • Narrowing to CargoPlane can drop calls

    If you declare the first parameter as CargoPlane instead of BaseEntity, Oxide will not call your hook when the spawned entity is not a CargoPlane (for example if another plugin replaced the prefab). Declare BaseEntity unless you need the typed members.

Build a plugin with OnCargoPlaneSignaled

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

Prompt: Announce incoming cargo planes to the whole server using the OnCargoPlaneSignaled 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 OnCargoPlaneSignaled

Generate