OnMapMarkerAdded
OnMapMarkerAdded is called after a map marker has been added to a player's client state and sent to their client. It fires on every marker the server creates for that player, including the marker a player places manually and server-generated markers such as death or explosion markers.
void OnMapMarkerAdded(BasePlayer player, ProtoBuf.MapNote mapNote)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who placed the map marker. Always the owner of the marker, so it is the correct target for replies, permissions checks and per-player limits. |
mapNote | ProtoBuf.MapNote | The ProtoBuf.MapNote that was just stored in the player's state. Its fields include type (a ProtoBuf.MapNote.MarkerType enum, e.g. Death, Explosion, Vendor, Note), x, y, and text for note markers. |
Return value
The original method returns void and the hook is called for its side effects only. Any value you return is ignored, so you cannot cancel or alter the marker from this hook.
How it works
The hook fires at the end of BasePlayer.Server_AddMarker, after the note is appended to the player's point-of-interest list, the player state is marked dirty, markers are resent to the client and team markers are updated. Because it sits at the tail of that method, the marker already exists in the player's state when your plugin runs; there is no cancellation path and no way to suppress the marker from this hook alone.
player is the owner of the state that received the marker, which is not necessarily the person who triggered it (death and explosion markers are examples). mapNote is the ProtoBuf.MapNote itself, so you can read type, coordinates and note text. The hook is synchronous and blocking; it runs inside the net write path that builds player state, so keep work short and never wait on external processes.
Declaring a more specific type is not useful here: the first parameter is dispatched against BasePlayer, so declaring BaseCombatEntity or a subclass would cause Oxide to skip calls where the instance does not match, and markers are only ever added for players. Do not edit or remove the note from inside the hook to block a marker; remove it with your own logic if needed and call DirtyPlayerState/SendMarkersToClient through supported APIs instead.
Example plugin: Announce map notes dropped by players
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Marker Announcer", "RustPluginGenerator", "1.0.0")]
[Description("Broadcasts and rate limits map notes that players place on the map")]
public class MarkerAnnouncer : RustPlugin
{
private class PluginConfig
{
[JsonProperty("Announce player map notes")]
public bool Announce { get; set; } = true;
[JsonProperty("Cooldown seconds per player")]
public float Cooldown { get; set; } = 5f;
}
private PluginConfig config;
private readonly Dictionary<ulong, float> lastAnnounce = new Dictionary<ulong, float>();
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"] = "{0} pinned a map note at {1}, {2}",
["Usage"] = "Usage: /markers <on|off>",
["Toggled"] = "Marker announcements are now {0}"
}, this);
}
private string Msg(string key, string userId = null, params object[] args)
{
return string.Format(lang.GetMessage(key, this, userId), args);
}
private void OnMapMarkerAdded(BasePlayer player, ProtoBuf.MapNote mapNote)
{
if (player == null || mapNote == null) return;
if (!config.Announce) return;
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (lastAnnounce.TryGetValue(player.userID, out last) && now - last < config.Cooldown) return;
lastAnnounce[player.userID] = now;
var pos = mapNote.worldPosition;
Server.Broadcast(Msg("Announce", null, player.displayName, Math.Round(pos.x), Math.Round(pos.z)));
}
[ChatCommand("markers")]
private void CmdMarkers(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!player.IsAdmin)
{
SendReply(player, Msg("Usage", player.UserIDString));
return;
}
if (args == null || args.Length == 0)
{
SendReply(player, Msg("Usage", player.UserIDString));
return;
}
bool enabled = args[0].Equals("on", StringComparison.OrdinalIgnoreCase);
config.Announce = enabled;
SaveConfig();
SendReply(player, Msg("Toggled", player.UserIDString, enabled ? "enabled" : "disabled"));
}
private void Unload()
{
lastAnnounce.Clear();
}
}
}The plugin listens for OnMapMarkerAdded and, when the Announce player map notes config option is true, broadcasts a message containing the player's display name and the rounded worldPosition.x and worldPosition.z of the marker. A per-player cooldown, keyed on player.userID and measured in seconds via Time.realtimeSinceStartup, prevents spam when a player places many markers in a row. The config exposes the cooldown length (Cooldown seconds per player, default 5 seconds) and the announce toggle. Admins can toggle announcements at runtime with /markers <on|off>, which saves the config; non-admins and calls without arguments receive a usage message. The cooldown dictionary is cleared in Unload.
Common use cases
Track player-placed markers
Log or broadcast team-wide notifications when a player pins a note marker, useful on PvE and roleplay servers where map pins are used for trade or meeting points.
Rate limit map spam
Count Note-type markers per player within a time window and warn or mute persistent spammers on your own side; the hook cannot remove the marker, so pair it with a manual cleanup if required.
Detect modded or foreign markers
Inspect
mapNote.typefor marker types your plugins do not create and flag clients that inject markers the server never produced.
Pitfalls
It also fires for server markers
Death, explosion and other markers are added through the same method, so filter on
mapNote.typebefore announcing unless you want every marker reported.You cannot cancel here
The note is already stored and sent before the hook runs and the return value is ignored; blocking a marker requires your own removal logic plus a state resend.
Keep it fast
The hook executes while player state is being written; avoid heavy loops or chat broadcasts on every marker without a throttle.
Build a plugin with OnMapMarkerAdded
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 OnMapMarkerAdded
Generate