OnMapMarkerRemove
OnMapMarkerRemove is called when a player deletes one of their own map markers, before the game disposes and removes it from their map state.
object OnMapMarkerRemove(BasePlayer player, List<ProtoBuf.MapNote> pointsOfInterest, int num)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer who is removing a marker from their personal map. Never null when the hook fires from the client RPC. |
pointsOfInterest | List<ProtoBuf.MapNote> | The player's current list of ProtoBuf.MapNote entries (their map markers), before the removal is applied. You can inspect names, colors and positions here. |
num | int | The zero-based index of the marker the client is removing. The game already validated that it is within range of the list. |
Return value
Returning null lets the game proceed: the marker is disposed, removed from the list, and the updated markers are sent to the client. Returning any non-null value cancels the removal, so the marker stays on the player's map and no state is marked dirty.
How it works
The hook fires inside BasePlayer.Server_RemovePointOfInterest, the server handler for the RPC a client sends when it drops a point of interest from its map. Before anything is deleted, the game checks that the marker list exists, that the requested index is valid, and only then calls the hook. At that moment the marker still exists in pointsOfInterest[num], so the ProtoBuf.MapNote you receive describes exactly what is about to disappear.
Use player to know whose map is changing, pointsOfInterest to inspect or count the remaining markers, and num to read the specific entry being removed (its note, colour and worldPosition). Blocking the call leaves the marker in place; the client keeps showing it because the server never sends the updated marker list.
The hook only fires for player-driven removals from the map UI. Marker clears handled through other paths do not reach this hook, so do not treat it as a full audit of map state changes. It is also a good place to enforce marker quotas or protected markers without touching the rest of the player's state.
Example plugin: Protect pinned markers from deletion
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using ProtoBuf;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Marker Guard", "RustPluginGenerator", "1.0.0")]
[Description("Prevents players from removing map markers tagged as protected.")]
public class MarkerGuard : RustPlugin
{
private const string PermBypass = "markerguard.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Protected name prefix")]
public string ProtectedPrefix = "[PIN]";
[JsonProperty("Allow admins to remove protected markers")]
public bool AllowAdminRemove = 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>
{
["Protected"] = "This marker is pinned and cannot be removed from the map.",
["NoPrefix"] = "The protected prefix in the config is empty; marker protection is 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(PermBypass, this);
if (string.IsNullOrEmpty(config.ProtectedPrefix))
PrintWarning(Msg("NoPrefix"));
}
private object OnMapMarkerRemove(BasePlayer player, List<MapNote> pointsOfInterest, int index)
{
if (player == null || pointsOfInterest == null) return null;
if (index < 0 || index >= pointsOfInterest.Count) return null;
if (string.IsNullOrEmpty(config.ProtectedPrefix)) return null;
var note = pointsOfInterest[index];
if (note == null) return null;
var text = GetNoteText(note);
if (string.IsNullOrEmpty(text)) return null;
if (!text.StartsWith(config.ProtectedPrefix, StringComparison.OrdinalIgnoreCase)) return null;
if (config.AllowAdminRemove && (player.IsAdmin || permission.UserHasPermission(player.UserIDString, PermBypass)))
return null;
player.ChatMessage(Msg("Protected", player.UserIDString));
return false;
}
// The marker label is carried on the ProtoBuf MapNote as the "label" string field.
private static string GetNoteText(MapNote note)
{
return note != null ? note.label : null;
}
}
}Server admins can pin markers by prefixing their name with the configured tag (default [PIN]). When a player tries to delete a marker, OnMapMarkerRemove reads the note at the given index and compares its label to the tag case-insensitively. If the label starts with the tag, the hook sends a localized chat message to the player and returns false, so the removal is stopped and the marker survives. If the label does not match, the player is an admin, or the player has the markerguard.bypass permission while AllowAdminRemove is enabled, the hook returns null and the normal deletion runs. Setting the prefix to an empty string disables protection and logs a warning at startup.
Common use cases
Enforce a maximum marker count
Read
pointsOfInterest.Countand reject removals that would drop a player below a minimum (for example, race checkpoints). Useful for event plugins that rely on map notes as progress markers.Protect admin-placed waypoints
Scan the note text for a reserved prefix or color and return non-null so players cannot delete markers set by staff for navigation or events.
Log marker removals for review
Record the player, note text, color and world position to a data file for moderation or analytics without changing game behavior.
Pitfalls
Index can equal the count during the removal cycle
The game checks
Count > numbefore calling, sopointsOfInterest[num]is valid, but subsequent removals shift the list. Do not cache the index across calls; always read the list supplied to the hook.Blocking is silent to the client
Returning non-null prevents the server from sending the updated list, so the marker reappears on the next marker sync. Tell the player through chat, otherwise the click looks like it did nothing.
Not all removals hit this hook
Only the client RPC path calls it. Server-side marker clears, disconnects and state resets do not, so a quota enforced here can still be bypassed by other code paths.
Build a plugin with OnMapMarkerRemove
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 OnMapMarkerRemove
Generate