OnMlrsTarget
OnMlrsTarget is called when a mounted player sets the MLRS target position, before that position is written to the launcher. Returning a non-null value cancels the assignment so the previous target stays in place.
object OnMlrsTarget(MLRS mlrs, UnityEngine.Vector3 worldPos, BasePlayer mountedPlayer)Parameters
| Parameter | Type | Description |
|---|---|---|
mlrs | MLRS | The MLRS entity whose operator is setting a target position. Use it to read OwnerID, the mounted player via GetMounted(), or its position. |
worldPos | UnityEngine.Vector3 | The requested impact position, already clamped to world bounds and snapped to terrain surface height. This is the value that would be stored as UserTargetHitPos. |
mountedPlayer | BasePlayer | The BasePlayer mounted on the MLRS at the time of the call (the operator). Can be null if the launcher is being driven without a player, so always null-check it. |
Return value
Return null to allow the target position to be written to the MLRS. Return any non-null value to stop the original path right there: UserTargetHitPos is left unchanged, so the launcher keeps its previous target and the subsequent server update block is skipped.
How it works
The hook fires inside MLRS.SetUserTargetHitPos, which is the game method that processes a new aim marker coming from the launcher's mounted operator. By the time the hook runs, the requested worldPos has already been clamped to the map bounds and its Y has been snapped to the terrain surface via GetSurfaceHeight, so you receive a usable ground position, not the raw cursor ray. Returning null lets the game assign UserTargetHitPos = worldPos and continue its server-side handling, which normally means replicating the new target to clients.
Because the call passes base._mounted, the mountedPlayer argument is the operator currently sitting in the launcher, not necessarily the owner of the entity — check mlrs.OwnerID if you care about ownership. Use this hook to validate range, block targeting of protected zones, enforce team rules, or log where players are aiming. Declaring BasePlayer for the third parameter is already the exact injected type; narrowing it further (for example to a custom subclass) would make Oxide skip the call for every other player. The hook can fire each time the player re-aims, so keep the body cheap and do not print to console inside it.
Example plugin: Deny MLRS targets inside protected zones and out of range
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Mlrs Target Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks MLRS target positions that fall inside protected zones or outside the allowed range")]
public class MlrsTargetGuard : RustPlugin
{
private const string PermBypass = "mlrstargetguard.bypass";
private PluginConfig config;
private class Zone
{
[JsonProperty("X")]
public float X { get; set; }
[JsonProperty("Y")]
public float Y { get; set; }
[JsonProperty("Z")]
public float Z { get; set; }
[JsonProperty("Radius")]
public float Radius { get; set; }
}
private class PluginConfig
{
[JsonProperty("Minimum distance from launcher (metres)")]
public float MinDistance { get; set; } = 50f;
[JsonProperty("Maximum distance from launcher (metres, 0 = unlimited)")]
public float MaxDistance { get; set; } = 0f;
[JsonProperty("Protected zones")]
public List<Zone> Zones { get; set; } = new List<Zone>
{
new Zone { X = 0f, Y = 0f, Z = 0f, Radius = 150f }
};
}
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>
{
["Blocked"] = "MLRS target denied: {0}",
["Zone"] = "impact point is inside a protected zone",
["Range"] = "impact point is outside the allowed range"
}, 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(PermBypass, this);
}
private object OnMlrsTarget(MLRS mlrs, Vector3 worldPos, BasePlayer mountedPlayer)
{
if (mlrs == null || config == null) return null;
if (mountedPlayer != null && permission.UserHasPermission(mountedPlayer.UserIDString, PermBypass)) return null;
string reason = null;
float distance = Vector3.Distance(mlrs.transform.position, worldPos);
if (distance < config.MinDistance || (config.MaxDistance > 0f && distance > config.MaxDistance))
{
reason = Msg("Range", mountedPlayer?.UserIDString);
}
else if (config.Zones != null)
{
foreach (var zone in config.Zones)
{
if (zone == null) continue;
float radius = Mathf.Max(0f, zone.Radius);
if (Vector3.Distance(new Vector3(zone.X, zone.Y, zone.Z), worldPos) <= radius)
{
reason = Msg("Zone", mountedPlayer?.UserIDString);
break;
}
}
}
if (reason == null) return null;
if (mountedPlayer != null && mountedPlayer.IsConnected)
{
mountedPlayer.ChatMessage(Msg("Blocked", mountedPlayer.UserIDString, reason));
}
return true;
}
}
}The plugin defines a list of protected zones as world-space spheres and a minimum/maximum distance measured from the launcher's own position, all configurable through oxide/config. When OnMlrsTarget fires, it checks that the mounted player exists and has the bypass permission; if not, it compares worldPos against each zone and measures the distance from mlrs.transform.position. A blocked request is rejected by returning a non-null value, which keeps the previous UserTargetHitPos, and the operator gets a localized chat message explaining why the aim was refused. Because the player can re-aim quickly, the hook performs only cheap distance math and a short list loop, and the config is read once at load.
Common use cases
Protect spawn areas and monuments
Reject target positions that fall inside defined no-fire zones so MLRS rockets cannot be dropped on spawn beaches, trade hubs or event arenas.
Enforce launcher range limits
Compare worldPos to the MLRS position and cancel the target if it is too close to the launcher or beyond a configured maximum distance.
Audit targeting for admins
Record the player, launcher position and requested impact point to a data file so staff can review where rockets were aimed without blocking anything.
Pitfalls
Non-null means deny
The hook uses exit-on-valid-type behavior, so even returning
falseor0stops the assignment. Always return null when you want the target to be accepted.Position is already snapped
worldPos has been clamped to map bounds and terrain height before the hook runs, so it will not match the raw player aim point or a position inside a structure; test against the snapped coordinates.
Mounted player can be null
The third argument mirrors the launcher's internal mounted reference and is not guaranteed to be set, so null-check it before reading UserIDString or sending chat messages.
Build a plugin with OnMlrsTarget
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 OnMlrsTarget
Generate