A lightweight, cross-platform add-on for Squaremap that allows server administrators to define custom map regions, borders, and markers using simple JSON configuration files.
Instead of relying on in-game banners or manual commands, squaremap-markers lets you permanently define towns, regions, pathways, markers, and custom icons directly from your server's file system.
squaremap >= 1.1.0 and minecraft >= 26.1. Please report any errors or
issues here.
- Multi-Platform: Runs on Paper (and Folia), Fabric, NeoForge, and Sponge.
- Multiple Files: Split your markers across multiple
.jsonfiles. - Rich Geometries: Native support for Rectangles, Polygons (with holes), Polylines, Ellipses, and Circles.
- Custom Icons: Drop image files into the
icons/folder to instantly use them on your map. - Interactive: Support for hover tooltips and clickable HTML hyperlinks.
- Hot-Reloading: Reload all files and icons from in-game with a single command.
- Ensure you have the latest version of Squaremap installed.
- Download the
squaremap-markersjar file for your specific platform. - Place the jar file into your server's
plugins/ormods/directory. - Restart the server.
- The configuration files are generated in:
- Paper:
plugins/SquaremapMarkers/ - Fabric, NeoForge, Sponge:
config/squaremap-markers/
- Paper:
| Command | Permission | Description |
|---|---|---|
/squaremapmarkers reload (alias /smm reload) |
squaremapmarkers.command.reload |
Reloads all JSON files and custom icons. |
You can create as many .json files as you want inside the main squaremap-markers folder. The plugin will scan the
folder and load them all.
Every file must start with a layers object. A single file can contain multiple layers, and a single layer can contain
multiple markers. Layers sharing an id across files are merged; a marker id repeated within the same layer replaces the
earlier one and is logged as a warning.
{
"layers": {
"layer_id_here": {
"name": "Display Name on Map",
"world": "minecraft:overworld",
"default_hidden": false,
"show_controls": true,
"z_index": 5,
"layer_priority": 0,
"markers": {
// Markers go here
}
}
}
}Layer ids, marker ids and icon names may only contain a-z A-Z 0-9 . _ -.
The world of a layer/marker is a dimension identifier such as minecraft:overworld,
minecraft:the_nether or mypack:mydim. An unqualified name like overworld is assumed to be in the minecraft
namespace. Identifiers are lowercase only.
name: Display name on the map. Defaults to the layer id.world: Default dimension for every marker in the layer. Markers may override it.options: Default styling for every marker in the layer, merged field by field with each marker's ownoptions. See below.default_hidden: Whether the layer starts hidden. Defaults tofalse.show_controls: Whether the layer appears in the map's layer control. Defaults totrue.z_index: Draw order on the map. Defaults to0.layer_priority: Ordering within the layer control. Defaults to0.
All marker types accept an optional options object to customize their appearance:
stroke: Whether to draw the outline at all. Defaults totrue.stroke_color: Hex color for the outline (e.g.,"#FF0000").stroke_weight: Thickness of the outline (integer).stroke_opacity: Outline transparency from0.0to1.0.fill: Whether to fill the shape at all. Defaults totrue.fill_color: Hex color for the inside of the shape.fill_opacity: Transparency from0.0(invisible) to1.0(solid).fill_rule:"nonzero"or"evenodd"— how self-intersections and holes are filled.hover_tooltip: Text shown when hovering over the marker.click_tooltip: HTML shown when clicking the marker (supports hyperlinks<a href="...">).
Squaremap's defaults if you omit them: stroke on, blue, weight 3, full opacity; fill on with
fill_opacity 0.2 and fill_rule evenodd (the fill colour follows the stroke colour unless you set it).
Exactly two points, the opposite corners. Order does not matter.
"pvp_arena": {
"type": "rectangle",
"points": [
{"x": -100, "z": -100},
{"x": 100, "z": 100}
]
}Polygons close themselves, so do not repeat the first point at the end — if you do, it is dropped.
Add an optional holes array (a list of point lists) to cut regions out of the shape; holes close
themselves the same way.
"town_border": {
"type": "polygon",
"points": [
{"x": 300, "z": 300},
{"x": 350, "z": 400},
{"x": 250, "z": 400}
]
}Use polyline if you want an open line, or a closed loop drawn as a line rather than a filled shape.
"main_highway": {
"type": "polyline",
"points": [
{"x": 0, "z": 0},
{"x": 1000, "z": 0}
]
}A circle takes a single radius; an ellipse takes radius_x and radius_z. All must be greater than 0.
"spawn_protection": {
"type": "circle",
"center": {"x": 0, "z": 0},
"radius": 50
}Use exactly one of:
icon— the name of an image in youricons/folder (without the extension).icon_key— a Squaremap icon key registered by Squaremap itself or another plugin, used verbatim. Squaremap only ships one:squaremap-spawn_icon.
Use size for a square icon, or size_x and size_z to size each axis separately.
"server_shop": {
"type": "icon",
"point": {"x": 50, "z": 50},
"icon": "treasure",
"size": 32
}An icon_key that is not registered renders as a broken image on the map, so it is logged as a warning during reload.
- Navigate to the
icons/directory inside the configuration folder. - Drop an image inside (e.g.,
treasure.png). - Run
/squaremapmarkers reloadin-game. - Set
"icon": "treasure"(the filename without its extension) in your JSON file.
Any image format this Java runtime can decode works — on a standard JDK that is PNG, JPEG, GIF, BMP, TIFF and WBMP.
The reload logs the marker count I expect, but the map does not change. The markers are registered; Squaremap has not published them yet. In order:
- Is the server ticking? Squaremap rebuilds
markers.jsonfrom a server tick, everymap.markers.update-interval-seconds(default5). A server paused bypause-when-empty-secondsinserver.properties(default60, so any empty server) never does, and neither does one under/tick freeze. The reload still succeeds and the markers are registered — they just cannot reach the map. Check with/tick query;/tick sprint 400publishes them immediately, andpause-when-empty-seconds=0stops it recurring. - Is something in front of Squaremap caching it? Ask Squaremap's own web server directly, bypassing any proxy:
curl -si localhost:8080/tiles/minecraft_overworld/markers.json. It sends anETagbut noCache-Controlon that path, so a reverse proxy or browser may hold on to an old copy. If that response is current and your site is not, the stale copy is in the proxy. - Is your web server reading the files instead? With
internal-webserver.enabled: trueandflush-json-immediately: false(both defaults),web/tiles/<world>/markers.jsonon disk is only rewritten when Squaremap stops. Setflush-json-immediately: trueif anything other than Squaremap serves that directory.
A marker is missing and the log says the world "is not enabled in Squaremap". Squaremap only tracks worlds with
map.enabled: true. Check the world value too — it is a dimension id such as minecraft:the_nether, not the level
folder name.
Run ./gradlew build in the root directory. Compiled jars will be located in the build/libs/ directory of each
platform's respective module.
The build targets Minecraft 26.3 and Java 25. Gradle provisions both the Java 25 toolchain and the Java 25 daemon JVM automatically, so no local JDK setup is required beyond being able to run the wrapper.
./gradlew test runs the marker-parsing tests in the core module. The parser is kept free of any
SquaremapProvider access so it can be tested without a running server.
Each of these boots a real server with this plugin/mod and a matching Squaremap already installed; the map is then served at http://localhost:8080.
./gradlew :paper:runServer
./gradlew :fabric:runServer
./gradlew :neoforge:runServer
./gradlew :sponge:runServer
The server directory is <module>/run/ (git-ignored). Minecraft requires you to accept the EULA before the first run of
each, e.g.:
mkdir -p paper/run && echo "eula=true" > paper/run/eula.txt
Publishing uses mod-publish-plugin and targets Modrinth and GitHub releases from one command. Remember to set MODRINTH_TOKEN and GITHUB_TOKEN.
- Set the version in
build.gradle.kts. A version containing-alpha/-betais published to the matching release channel automatically; anything else is published as stable. - Add a
## [<version>] - <date>section at the top ofCHANGELOG.md. ./gradlew clean build, and ideally./gradlew :paper:runServerto smoke-test../gradlew publishMods -PpublishDryRun=trueto review, then without the flag to publish.