How to make a Rust map grid location plugin (chat /grid command)
A /grid chat command that converts a player's world position into a Rust map grid cell (like G12) using the server's world size, with exact coordinates shown only to admins. It uses OnServerInitialized to cache the world size and OnPlayerConnected to announce the player's grid on join.
Vanilla Rust shows a grid reference in the map UI but gives you no chat command to print it, so players who call out coordinates in voice or text often read the grid wrong, and admins handling reports have no quick way to ask where someone is. This plugin adds a /grid chat command that reads player.transform.position, converts it to the map's grid index using ConVar.Server.worldsize, and replies with a letter/number cell such as G12. The number column counts from the south, the letter row from the west, matching the in-game map grid. Admins and players holding the mapgridlocation.coords permission also see the raw X/Z coordinates. The world size is read once in OnServerInitialized, the grid is announced to each player on OnPlayerConnected, and the config lets you change the join announcement, the cell count and whether the grid is shown.
The plugin code
using System;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Map Grid Location", "RustPluginGenerator", "1.0.0")]
[Description("Adds a /grid command that reports the player's map grid cell and shows exact coordinates to admins.")]
public class MapGridLocation : RustPlugin
{
private const string PermUse = "mapgridlocation.use";
private const string PermCoords = "mapgridlocation.coords";
private PluginConfig config;
private float worldSize;
private float cellSize;
private readonly List<Timer> timers = new List<Timer>();
private class PluginConfig
{
[JsonProperty("Announce On Connect")]
public bool AnnounceOnConnect { get; set; } = true;
[JsonProperty("Grid Cells")]
public int GridCells { get; set; } = 30;
[JsonProperty("Show Grid In Reply")]
public bool ShowGridInReply { 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>
{
["GridReply"] = "You are in grid {0}.",
["GridReplyCoords"] = "You are in grid {0} (X: {1}, Z: {2}, Y: {3}).",
["GridUnknown"] = "Could not work out your grid position.",
["NoPermission"] = "You do not have permission to use this command.",
["Announce"] = "You are currently in grid {0}."
}, 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(PermUse, this);
permission.RegisterPermission(PermCoords, this);
}
private void OnServerInitialized()
{
worldSize = ConVar.Server.worldsize;
RefreshCellSize();
}
private void RefreshCellSize()
{
int cells = config.GridCells <= 0 ? 30 : config.GridCells;
if (worldSize <= 0f) worldSize = ConVar.Server.worldsize;
if (worldSize <= 0f) worldSize = 3000f;
cellSize = worldSize / cells;
}
private string PositionToGrid(Vector3 pos)
{
if (cellSize <= 0f) RefreshCellSize();
int cells = config.GridCells <= 0 ? 30 : config.GridCells;
float half = worldSize * 0.5f;
float shiftedX = (pos.x + half) / cellSize;
float shiftedZ = (pos.z + half) / cellSize;
int column = Mathf.FloorToInt(shiftedX);
int row = Mathf.FloorToInt(shiftedZ);
if (column < 0) column = 0;
if (column > cells - 1) column = cells - 1;
if (row < 0) row = 0;
if (row > cells - 1) row = cells - 1;
char letter = (char)('A' + row);
return letter + (column + 1).ToString();
}
private string BuildReply(BasePlayer player, string userId, bool withCoords)
{
Vector3 pos = player.transform.position;
string grid = PositionToGrid(pos);
string coords = string.Format("{0}, {1}, {2}",
Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.z), Mathf.RoundToInt(pos.y));
if (!config.ShowGridInReply)
{
return withCoords
? string.Format("X: {0}, Z: {1}, Y: {2}", Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.z), Mathf.RoundToInt(pos.y))
: Msg("GridReply", userId, grid);
}
if (withCoords)
return Msg("GridReplyCoords", userId, grid, Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.z), Mathf.RoundToInt(pos.y));
return Msg("GridReply", userId, grid);
}
[ChatCommand("grid")]
private void CmdGrid(BasePlayer player, string command, string[] args)
{
if (player == null) return;
string userId = player.UserIDString;
if (!permission.UserHasPermission(userId, PermUse))
{
SendReply(player, Msg("NoPermission", userId));
return;
}
bool withCoords = permission.UserHasPermission(userId, PermCoords) || player.IsAdmin;
SendReply(player, BuildReply(player, userId, withCoords));
}
private void OnPlayerConnected(BasePlayer player)
{
if (player == null || !config.AnnounceOnConnect) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return;
Timer t = timer.Once(3f, () =>
{
if (player == null || !player.IsConnected) return;
SendReply(player, Msg("Announce", player.UserIDString, PositionToGrid(player.transform.position)));
});
timers.Add(t);
}
private void Unload()
{
foreach (Timer t in timers)
{
if (t != null) t.Destroy();
}
timers.Clear();
}
}
}Hooks used: OnServerInitialized, OnPlayerConnected
Config file
Created automatically on first load at oxide/config/MapGridLocation.json.
{
"Announce On Connect": true,
"Grid Cells": 30,
"Show Grid In Reply": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/grid | Replies with your current map grid cell. Adds exact X/Z coordinates when you have the admin permission. | mapgridlocation.use |
| Permission | Grants |
|---|---|
mapgridlocation.use | Grants access to the /grid chat command and the connect announcement. |
mapgridlocation.coords | Adds exact X/Z world coordinates and height to the /grid reply. |
How the code works
Cache the world size once
The grid is drawn from the world origin to the world edge, so the plugin needs
ConVar.Server.worldsize.OnServerInitializedcopies it into thefloat worldSizefield and callsRefreshCellSize, which divides it by the configGrid Cellsvalue (default 30) to fill thefloat cellSizefield. Doing this once avoids reading the convar on every command call. IfGrid Cellsis zero or negative, the helper falls back to 30.Convert a position to a grid cell
Positions run from
-worldSize/2to+worldSize/2on both axes.PositionToGridaddsworldSize * 0.5fto X and Z to shift the numbers to 0-based, divides bycellSize, floors withMathf.FloorToInt, and clamps to the 0..(cells-1) range. The letter comes from the Z row ((char)('A' + row)) so the letter row counts from the south and the number column counts from the east, matching the map. The result is a string likeG12.The /grid chat command
[ChatCommand("grid")]declaresCmdGrid(BasePlayer player, string command, string[] args). It checkspermission.UserHasPermissionformapgridlocation.useand answers viaBuildReply. That helper readsplayer.transform.position, builds the cell, and chooses the lang keyGridReplyorGridReplyCoords. A player withmapgridlocation.coordsor theIsAdminflag gets the coordinates variant. An alternative reply without the grid is built whenShow Grid In Replyis false.Announcing the grid on join
OnPlayerConnected(BasePlayer player)fires when a player finishes connecting. IfAnnounce On Connectis true and the player hasmapgridlocation.use, the plugin schedules a one-shottimer.Once(3f, ...)so the message lands after connection spam. Inside the callback it checksIsConnectedagain and sends theAnnouncelang key. The timer is added to thetimerslist soUnloadcan destroy it if the plugin is reloaded mid-delay.Config and language files
PluginConfigholdsAnnounce On Connect,Grid CellsandShow Grid In Reply, mapped with[JsonProperty]names.LoadConfigreads the object, prints a warning and reloads defaults when parsing fails, then saves.LoadDefaultMessagesregistersGridReply,GridReplyCoords,GridUnknown,NoPermissionandAnnounce, so an owner can edit translations inoxide/lang/en/MapGridLocation.jsonwithout recompiling.Handling edge cases
If
cellSizeis not positive when a command runs,PositionToGridcallsRefreshCellSize, which re-readsConVar.Server.worldsizeand falls back to 3000 when the convar is unset. Column and row values outside the grid are clamped to the nearest valid cell rather than producing an out-of-range letter.Unloadwalks thetimerslist, callsDestroy()on eachTimer, and clears the list so a reload does not leave a stale callback.
Install it on your server
Save MapGridLocation.cs
Keep the file name exactly as the class name. Oxide matches them.Upload it to the plugins folder
Oxide:oxide/plugins/. Carbon:carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.Edit the config if you want
On first load the default config is written tooxide/config/MapGridLocation.json(Carbon:carbon/configs/MapGridLocation.json). After editing, reload it:oxide.reload MapGridLocation c.reload MapGridLocation # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default mapgridlocation.use c.grant group default mapgridlocation.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Show grid to teammates
Add a /teamgrid command that prints every online team member's grid for the caller.
Generate this versionBroadcast admin coordinate request
Let staff query another player's grid by name.
Generate this versionAuto-grid on clan chat
Prefix chat messages with the sender's grid when the message starts with the grid prefix.
Generate this version
FAQ
Which axis maps to the letter and which to the number?
On the vanilla Rust map the letters run north-south (Z) and the numbers run east-west (X). The plugin divides player.transform.position.x by cellSize to get the number column and transform.position.z by cellSize to get the letter row, matching the in-game grid so the /grid result lines up with the map overlay.
Does it work on custom-size or custom-cell maps?
Set Grid Cells to the number of divisions your map overlay draws (usually 30). The plugin divides ConVar.Server.worldsize by that number, so a 4000-size map with 30 cells gives cells of roughly 133 units, which matches the Rust map. If another plugin redraws the grid, set Grid Cells to its division count.
Does the plugin store or log positions?
No. Every /grid call computes the cell from the live position and sends a chat reply. Nothing is written to oxide/data and nothing is printed to the server console. If you want a log, add a Puts line in CmdGrid and it will appear in the console output.
How do I install it?
Save the plugin as MapGridLocation.cs, drop it into oxide/plugins/, and run oxide.reload MapGridLocation (or restart the server). On Carbon, place the same file in carbon/plugins/. The default config is created on first load, and you can add the permissions with oxide.grant.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More utility plugins
- Command aliasesThis plugin maps short chat commands like /h and /k to longer ones such as /home and /kit, defined entirely in its config file, using theโฆOnServerInitializedOnPlayerCommandOnPlayerDisconnected
- Info commandsA config-driven text command plugin: every entry in the config defines a command name and a list of chat lines, and the plugin registers them allโฆ
- Online players listAdds a /players chat command that prints an alphabetically sorted, paginated list of online players with online, sleeping and joining counts; adminsโฆ
Last updated Oct 26, 2026.
Customize map grid location
Customize