Skip to content
RustPluginGenerator

OnCargoShipHarborArrived

OnCargoShipHarborArrived is called when a CargoShip reaches a harbor dock and the game sets up its harbor stay. It fires from CargoShip.OnArrivedAtHarbor, after the ship's harbor flags are set and just before it schedules its departure timer.

Signature
void OnCargoShipHarborArrived(CargoShip cargoShip)

Parameters

Hook parameters
ParameterTypeDescription
cargoShipCargoShipThe CargoShip entity that just docked at a harbor. Use it for its position, net.ID, and as a key to track per-ship state; null-check it before use.

Return value

The return value is ignored. CargoShip.OnArrivedAtHarbor is a void method with a plain continue injection, so returning null or any value does not change the ship's docking behavior or the harbor timer.

How it works

This hook fires once each time a cargo ship finishes its approach and arrives at one of the map's harbor docking points. The game has already updated the ship's internal harbor state (including its reserved flags) and has scheduled the leave-harbor timer when the hook is called. Use it to react to the event: announce arrivals to players, record which ship and harbor were involved, spawn custom loot crates near the dock, adjust rewards, or flag the ship for custom behavior while it sits in port.

The single parameter is the CargoShip itself, so you can read its position, prefab, and network ID, and track state per ship in a dictionary keyed by net.ID.Value. Since the method takes a concrete type, Oxide only calls your hook when the argument is a CargoShip, so you do not need to cast.

Arrivals typically happen on a fixed cycle, so treat the hook as a timer-like event rather than a per-frame callback. Keep the work light: avoid heavy enumeration of world entities and never call Puts or PrintWarning here.

Example plugin: Announce cargo ship arrivals and reward nearby players

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

namespace Oxide.Plugins
{
    [Info("Cargo Harbor Announcer", "RustPluginGenerator", "1.0.0")]
    [Description("Announces cargo ship harbor arrivals and rewards nearby players")]
    public class CargoHarborAnnouncer : RustPlugin
    {
        private const string PermReward = "cargoharborannouncer.reward";

        private PluginConfig config;
        private readonly HashSet<ulong> announcedShips = new HashSet<ulong>();

        private class PluginConfig
        {
            [JsonProperty("Announce arrivals server-wide")]
            public bool AnnounceArrivals = true;

            [JsonProperty("Proximity radius in meters")]
            public float Radius = 60f;

            [JsonProperty("Reward item shortname")]
            public string RewardShortname = "scrap";

            [JsonProperty("Reward amount")]
            public int RewardAmount = 25;

            [JsonProperty("Require reward permission")]
            public bool RequireRewardPermission = false;
        }

        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>
            {
                ["Announce"] = "A cargo ship has docked at coordinates {0}.",
                ["Nearby"] = "The cargo ship docked nearby. Check the harbor!",
                ["Reward"] = "You received {0} x {1} for being near the dock."
            }, 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(PermReward, this);
        }

        private void Unload()
        {
            announcedShips.Clear();
        }

        private void OnCargoShipHarborArrived(CargoShip cargoShip)
        {
            if (cargoShip == null || cargoShip.net == null) return;

            ulong id = cargoShip.net.ID.Value;
            if (!announcedShips.Add(id)) return;

            Vector3 dockPos = cargoShip.transform.position;

            if (config.AnnounceArrivals)
            {
                string coords = FormatCoords(dockPos);
                Server.Broadcast(Msg("Announce", null, coords));
            }

            if (string.IsNullOrEmpty(config.RewardShortname) || config.RewardAmount <= 0) return;

            float radiusSqr = config.Radius * config.Radius;
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null || !player.IsConnected || player.IsDead()) continue;
                if ((player.transform.position - dockPos).sqrMagnitude > radiusSqr) continue;

                if (config.RequireRewardPermission &&
                    !permission.UserHasPermission(player.UserIDString, PermReward)) continue;

                Item item = ItemManager.CreateByName(config.RewardShortname, config.RewardAmount);
                if (item == null) continue;

                player.GiveItem(item);
                player.ChatMessage(Msg("Reward", player.UserIDString, config.RewardAmount, item.info.displayName.english));
            }
        }

        private static string FormatCoords(Vector3 pos)
        {
            int x = Mathf.RoundToInt(pos.x);
            int z = Mathf.RoundToInt(pos.z);
            return $"X {x}, Z {z}";
        }
    }
}

The plugin listens for OnCargoShipHarborArrived and records each arriving ship by its network ID so it can report the position. On arrival it builds a server-wide broadcast using a localized message with no player context, then scans BasePlayer.activePlayerList for players within the configured radius of the ship. Each eligible player gets a chat message and, if a permission is set and they hold it, a direct item reward through inventory.GiveItem. Config fields let admins tune the announcement toggle, the proximity radius, and the rewarded item. Every hook parameter is null-checked before use, and permissions are registered in Init.

Common use cases

  • Server-wide arrival announcements

    Broadcast to all players when a cargo ship docks, optionally including a rough grid location so groups know where to head.

  • Reward nearby players

    Give a small item reward or bonus to players standing near the dock when the ship arrives, throttled by permission or a cooldown.

  • Track harbor cycles

    Store arrival timestamps per ship network ID to measure cargo ship dock intervals and expose them through an admin console command.

Pitfalls

  • Do not expect to affect the docking

    The hook fires after the harbor state is set and the leave timer is scheduled, and its return value is ignored. You cannot cancel or delay arrival from here.

  • Same ship can arrive repeatedly

    A cargo ship visits multiple harbors over its lifetime, so the hook can fire many times for the same entity. Key your tracking on net.ID.Value and decide whether to reset existing entries.

  • Guard against missing entity data

    The cargoShip argument can be null in edge cases such as unload timing. Always null-check before reading net.ID or transform.position to avoid errors during the hook.

Build a plugin with OnCargoShipHarborArrived

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

Prompt: Server-wide arrival announcements using the OnCargoShipHarborArrived 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 OnCargoShipHarborArrived

Generate