OnMissionStarted
OnMissionStarted is called when a mission is started for a player, right after BaseMission.MissionStart finishes spawning the mission's start entities. It gives the mission object, the player's mission instance and the assigned player.
void OnMissionStarted(BaseMission mission, BaseMission.MissionInstance missionInstance, BasePlayer assignee)Parameters
| Parameter | Type | Description |
|---|---|---|
mission | BaseMission | The BaseMission entity that owns the start logic, e.g. the mission object spawned in the world. Use it to read the mission definition, its short prefab name or its position. |
missionInstance | BaseMission.MissionInstance | The BaseMission.MissionInstance for the player who accepted the mission; it holds the per-player runtime state such as spawned mission entities and progress. |
assignee | BasePlayer | The BasePlayer who the mission was just started for. This is the player to notify, reward or track. |
Return value
The hook is called after the original method body has already run and its return value is ignored. Returning null or any value changes nothing; the mission start completes normally either way.
How it works
The hook fires at the very end of BaseMission.MissionStart, after the method has walked its mission entity entries and spawned the ones flagged with spawnOnMissionStart for the assignee. That means by the time your plugin sees the call, the mission's opening world objects (markers, crates, NPCs, whatever the mission definition lists) already exist.
Use mission to identify which mission type started (check ShortPrefabName, the prefab path, or the concrete class via as), missionInstance to reach per-player state such as spawned mission entities, and assignee to act on the player: send a message, start tracking, teleport, or hand out gear.
The parameter is typed BaseMission, so declaring a more specific class only helps if the mission object in the world is that type; Oxide skips your method when the passed instance is not assignable to your parameter type. Mission instances are also referenced by the mission object, so treat missionInstance as read-mostly and avoid mutating it from this hook.
Fires once per player assignment, so it is safe to do per-player work here rather than broadcasting.
Example plugin: Announce and reward players when they start a mission
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Mission Start Notifier", "RustPluginGenerator", "1.0.0")]
[Description("Notifies players when they start a mission and rewards repeated mission starts")]
public class MissionStartNotifier : RustPlugin
{
private const string PermReward = "missionstartnotifier.reward";
private PluginConfig config;
private readonly Dictionary<ulong, int> startCounts = new Dictionary<ulong, int>();
private class PluginConfig
{
[JsonProperty("Announce to player")]
public bool Announce { get; set; } = true;
[JsonProperty("Reward item shortname")]
public string RewardShortname { get; set; } = "scrap";
[JsonProperty("Reward amount")]
public int RewardAmount { get; set; } = 50;
[JsonProperty("Starts per reward")]
public int StartsPerReward { get; set; } = 5;
[JsonProperty("Only players with permission get rewards")]
public bool PermissionOnly { 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>
{
["Started"] = "Mission started: {0}",
["Reward"] = "You received {0} x{1} for starting {2} missions.",
["RewardUnknown"] = "Mission started. A reward item could not be given."
}, 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()
{
startCounts.Clear();
}
private void OnMissionStarted(BaseMission mission, BaseMission.MissionInstance missionInstance, BasePlayer assignee)
{
if (mission == null || missionInstance == null || assignee == null || assignee.IsNpc)
return;
string missionName = string.IsNullOrEmpty(mission.name) ? "unknown" : mission.name;
string userId = assignee.UserIDString;
if (config.Announce)
assignee.ChatMessage(Msg("Started", userId, missionName));
int count;
startCounts.TryGetValue(assignee.userID.Get(), out count);
count++;
startCounts[assignee.userID.Get()] = count;
if (config.StartsPerReward <= 0 || count % config.StartsPerReward != 0)
return;
if (config.PermissionOnly && !permission.UserHasPermission(userId, PermReward))
return;
Item reward = ItemManager.CreateByName(config.RewardShortname, config.RewardAmount);
if (reward == null)
{
assignee.ChatMessage(Msg("RewardUnknown", userId));
return;
}
assignee.GiveItem(reward);
assignee.ChatMessage(Msg("Reward", userId, reward.info.displayName.english, config.RewardAmount, count));
}
}
}The plugin listens for OnMissionStarted and checks that the assignee and mission are valid before doing anything. It reads the mission's name (falling back to "unknown" when empty) and, if Announce is enabled, sends a localised chat message to the player. Each started mission is counted per player, and whenever the count is a multiple of the configured StartsPerReward value the plugin grants a configured reward item through ItemManager.CreateByName and gives it directly to the player. If PermissionOnly is enabled, only players with the missionstartnotifier.reward permission receive rewards. If the configured shortname does not resolve to an item, the player gets the RewardUnknown message instead. The config exposes the reward shortname, amount, starts per reward and the announcement and permission toggles. Counts are a simple in-memory dictionary, reset on plugin unload, so no data files are needed. All player-facing text goes through the language file, keeping the messages easy to translate.
Common use cases
Mission tracker UI
Push a CUI panel to the assignee showing the mission name and a short objective list, then tear it down in OnMissionSucceeded or OnMissionFailed.
Progress counters and rewards
Count started missions per player and grant items, economics balance or a permission group after a threshold is reached.
Type-specific handling
Check the mission's ShortPrefabName or cast
missionto a known class to apply different rules such as teleporting the player to a starting area for one mission type only.
Pitfalls
Mission entities already spawned
The hook fires after start entities exist, so spawning your own copies creates duplicates. Modify or remove existing entities instead of adding new ones.
Assigning a mission reassigns it
Calling mission start APIs from this hook can trigger another OnMissionStarted call for the same player. Guard against re-entry with a flag or cooldown per player.
Instance data is per player
A single mission object can serve many players; never store per-player state on
missionitself. Key your own data byassignee.userID.Get()or usemissionInstance.
Build a plugin with OnMissionStarted
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 OnMissionStarted
Generate