Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ jobs:
- run: ./gradlew build
- uses: actions/upload-artifact@v4
with:
name: BlueMapHUD
name: BlueMinimap
path: build/libs/*.jar
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" build/libs/BlueMapHUD-*.jar \
--title "BlueMapHUD $GITHUB_REF_NAME" --notes-file "docs/releases/$GITHUB_REF_NAME.md"
gh release create "$GITHUB_REF_NAME" build/libs/BlueMinimap-*.jar \
--title "BlueMinimap $GITHUB_REF_NAME" --notes-file "docs/releases/$GITHUB_REF_NAME.md"
66 changes: 38 additions & 28 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group = "com.extantape"
version = "0.1.0"
version = "0.2.0"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
Expand Down Expand Up @@ -52,6 +52,6 @@ tasks.test {
}

tasks.jar {
archiveBaseName.set("BlueMapHUD")
archiveBaseName.set("BlueMinimap")
from("LICENSE")
}
10 changes: 5 additions & 5 deletions docs/26x-audit.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 26.x audit

BlueMapHUD is a Paper plugin for Paper 26.1 and newer only: `paper-plugin.yml`, `api-version: '26.1'`, Java 25, and the Paper 26.1.2 API at compile time. This note records what changed when 1.21.11 support was dropped and which newer Paper features were checked. Checked against docs.papermc.io on 2026-11-07.
BlueMinimap is a Paper plugin for Paper 26.1 and newer only: `paper-plugin.yml`, `api-version: '26.1'`, Java 25, and the Paper 26.1.2 API at compile time. This note records what changed when 1.21.11 support was dropped and which newer Paper features were checked. Checked against docs.papermc.io on 2026-11-07.

## What changed

Expand All @@ -16,7 +16,7 @@ BlueMapHUD is a Paper plugin for Paper 26.1 and newer only: `paper-plugin.yml`,

Paper has no 26.1.0 build. Its 26.1 line is 26.1.1 (alpha builds only) and 26.1.2 (stable), so "26.1" in practice means 26.1.2. BlueMap 5.29 itself declares `api-version: 26.1.1`.

**`paper-plugin.yml`.** Paper still marks Paper plugins experimental, and its docs don't say whether one can use a `plugin.yml` plugin's classes. BlueMap is one, so this was tested rather than assumed. BlueMap is declared as a required server dependency that loads first with `join-classpath: true`. Floodgate is declared the same way but optional, because the plugin finds it by reflection. There is no `commands` section: the commands were already Brigadier, registered through `LifecycleEvents.COMMANDS`. Paper plugins get their own classloader, so other plugins can't reach BlueMapHUD's classes. None need to.
**`paper-plugin.yml`.** Paper still marks Paper plugins experimental, and its docs don't say whether one can use a `plugin.yml` plugin's classes. BlueMap is one, so this was tested rather than assumed. BlueMap is declared as a required server dependency that loads first with `join-classpath: true`. Floodgate is declared the same way but optional, because the plugin finds it by reflection. There is no `commands` section: the commands were already Brigadier, registered through `LifecycleEvents.COMMANDS`. Paper plugins get their own classloader, so other plugins can't reach BlueMinimap's classes. None need to.

**Java 25** found one real compile error: a cache record named `Entry` clashed with `LinkedHashMap.Entry` inside an anonymous subclass. It's renamed to `CachedTile`.

Expand Down Expand Up @@ -46,10 +46,10 @@ All on 2026-11-07, with BlueMap 5.29. The 31 unit tests pass on Java 25. The tes
| Load, 20 players for a minute | 9/9 | 9/9 | 9/9 |
| `public-url` and the hosted-pack check | | | 36/36 |
| BlueMap on SQLite through the HTTP source | | | 1024/1024 cells |
| BlueMap starting next to BlueMapHUD | | | 8/8 starts |
| BlueMap starting next to BlueMinimap | | | 8/8 starts |

The live suite covers BlueMap's classes being reachable from the Paper plugin (tiles, markers, hidden players), commands and their back-off, `/bmhud reload`, `/bluemap reload` and a full `/reload`.
The live suite covers BlueMap's classes being reachable from the Paper plugin (tiles, markers, hidden players), commands and their back-off, `/blueminimap reload`, `/bluemap reload` and a full `/reload`.

**Mocked or not tested:** the client is a protocol bot speaking 1.21.11 through ViaBackwards, not a real 26.x client. Magic's pack call is simulated with the same Bukkit method. Floodgate was not installed, so the optional `join-classpath` for it is untested. Paper 26.1.1, which only had alpha builds, was not tested.

**Found along the way:** `/bmhud debug dump` could finish after the plugin was disabled and then try to schedule its reply, which threw an error. It now checks first, as frame building already did.
**Found along the way:** `/blueminimap debug dump` could finish after the plugin was disabled and then try to schedule its reply, which threw an error. It now checks first, as frame building already did.
1 change: 1 addition & 0 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ Owner decisions for BlueMapHUD, newest first.
| 2026-11-07 | Coordinates stay hidden during boss fights, along with the minimap. | A second boss bar must never appear, and showing them on the action bar instead could flicker with other plugins. |
| 2026-11-07 | Add `hud.default-mode: map\|coords` to `config.yml`, defaulting to `map`. | A server replacing a coordinates plugin can start new players on coordinates only. A player's own `/hud mode` choice always wins. |
| 2026-11-06 | Add a coordinates-only mode (`/hud mode coords`) in the same single boss bar. | Players can keep coordinates without the map, so a server doesn't need a separate coordinates plugin. |
- **Name (2026-11-08, owner):** BlueMapHUD is renamed BlueMinimap, one word in every place: repo, plugin name, jar, package, admin command and prose, described as "a BlueMap minimap and coordinates HUD for Paper, rendered in game", an add-on to BlueMap. Released as 0.2.0 together with HouseRules 0.2.0. Identifiers players' data depends on keep their old values for good: the saved-settings keys `bluemaphud:settings` and `bluemaphud:theme`, and the pack ID from `bluemaphud:pack`. Old commands and permissions work for one release; for default-on permissions a "no" on either name wins, so children's groups that were stopped from sharing stay stopped. With HouseRules older than 0.2, `/hud share` is turned off (fail closed).
12 changes: 12 additions & 0 deletions docs/releases/v0.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BlueMapHUD is now called BlueMinimap. This release renames it and changes nothing else in how the map works. The owner chose the name on 2026-11-08.

This is an early 0.x release. It has passed bot tests on throwaway servers, but it hasn't been seen in a real game client yet.

## Upgrading from BlueMapHUD 0.1.0

- Swap `BlueMapHUD-0.1.0.jar` for `BlueMinimap-0.2.0.jar` with the server stopped. **Update HouseRules to 0.2.0 at the same time** if you use it: with an older HouseRules, BlueMinimap turns `/hud share` off and logs why, so shares can't skip chat circles.
- On first start, `plugins/BlueMapHUD/` is copied to `plugins/BlueMinimap/` if that doesn't exist yet. The old folder is left untouched as a backup. Nothing is ever overwritten.
- The pack is now `plugins/BlueMinimap/pack/blueminimap-pack.zip`, and its contents changed, so its SHA-1 changed. If you host it yourself, upload the new file. Players download it once.
- Players keep their HUD settings: they're stored under the same keys as before.
- **Deprecated, removed in the next release:** `/bmhud`, `/bluemaphud:map`, `/bluemaphud:hud` and the `bluemaphud.*` permissions. For permissions that are on by default, a "no" on either name wins, so a group you stopped from sharing stays stopped. Differences between the old and new names are logged once per player.
- Changed outright: the plugin, jar and folder are `BlueMinimap`, the admin command is `/blueminimap`, and the share event is `com.extantape.blueminimap.api.CoordinateShareEvent` (same methods).
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rootProject.name = "BlueMapHUD"
rootProject.name = "BlueMinimap"

// Test-only plugins for the live tests in test/live. They're never shipped.
include("live-test-helper", "live-test-conflict")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package com.extantape.bluemaphud;
package com.extantape.blueminimap;

import com.extantape.bluemaphud.command.HudCommands;
import com.extantape.bluemaphud.config.HudConfig;
import com.extantape.bluemaphud.hud.FrameBuilder;
import com.extantape.bluemaphud.hud.HudManager;
import com.extantape.bluemaphud.hud.HudSession;
import com.extantape.bluemaphud.markers.MarkerService;
import com.extantape.bluemaphud.pack.PackBuilder;
import com.extantape.bluemaphud.pack.PackService;
import com.extantape.bluemaphud.render.HudFonts;
import com.extantape.bluemaphud.render.IconSet;
import com.extantape.bluemaphud.render.MapComposer;
import com.extantape.bluemaphud.share.ShareMenu;
import com.extantape.bluemaphud.share.ShareService;
import com.extantape.bluemaphud.theme.Theme;
import com.extantape.bluemaphud.theme.ThemeSet;
import com.extantape.bluemaphud.tiles.FileTileSource;
import com.extantape.bluemaphud.tiles.GridCache;
import com.extantape.bluemaphud.tiles.HttpTileSource;
import com.extantape.bluemaphud.tiles.TileSource;
import com.extantape.bluemaphud.tiles.TileStore;
import com.extantape.blueminimap.command.HudCommands;
import com.extantape.blueminimap.config.HudConfig;
import com.extantape.blueminimap.hud.FrameBuilder;
import com.extantape.blueminimap.hud.HudManager;
import com.extantape.blueminimap.hud.HudSession;
import com.extantape.blueminimap.markers.MarkerService;
import com.extantape.blueminimap.pack.PackBuilder;
import com.extantape.blueminimap.pack.PackService;
import com.extantape.blueminimap.render.HudFonts;
import com.extantape.blueminimap.render.IconSet;
import com.extantape.blueminimap.render.MapComposer;
import com.extantape.blueminimap.share.ShareMenu;
import com.extantape.blueminimap.share.ShareService;
import com.extantape.blueminimap.theme.Theme;
import com.extantape.blueminimap.theme.ThemeSet;
import com.extantape.blueminimap.tiles.FileTileSource;
import com.extantape.blueminimap.tiles.GridCache;
import com.extantape.blueminimap.tiles.HttpTileSource;
import com.extantape.blueminimap.tiles.TileSource;
import com.extantape.blueminimap.tiles.TileStore;
import de.bluecolored.bluemap.api.BlueMapAPI;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import net.kyori.adventure.text.Component;
Expand All @@ -46,7 +46,7 @@
import java.util.function.Consumer;
import java.util.logging.Level;

public final class BlueMapHudPlugin extends JavaPlugin implements Listener {
public final class BlueMinimapPlugin extends JavaPlugin implements Listener {
/** Runs after other plugins, so the command name chains can see what they took. */
private static final int COMMANDS_PRIORITY = 1000;
private static final long PACK_SEND_DELAY_TICKS = 20;
Expand All @@ -65,6 +65,17 @@ public final class BlueMapHudPlugin extends JavaPlugin implements Listener {

@Override
public void onEnable() {
Perms.logTo(line -> getLogger().warning(line));
java.nio.file.Path folder = getDataFolder().toPath();
String moved = Migration.copyIfMissing(folder.resolveSibling("BlueMapHUD"), folder);
if (moved != null) {
getLogger().warning(moved);
try { // The old pack file would only confuse whoever hosts the pack; the new one is built below.
java.nio.file.Files.deleteIfExists(folder.resolve("pack/bluemaphud-pack.zip"));
} catch (java.io.IOException e) {
getLogger().warning("Couldn't remove the copied old pack file: " + e.getMessage());
}
}
saveDefaultConfig();
config = HudConfig.from(getConfig());
packs = new PackService(this, getDataFolder().toPath().resolve("pack"));
Expand Down Expand Up @@ -150,7 +161,7 @@ public void explainIfNoMap(Player player) {
/** Coordinates need the pack but not a map, so this is all that can stop them appearing. */
public void explainIfNoPack(Player player) {
if (!packs.ready(player)) {
player.sendMessage(Component.text("Your client hasn't loaded the BlueMapHUD resource pack yet.", NamedTextColor.YELLOW));
player.sendMessage(Component.text("Your client hasn't loaded the BlueMinimap resource pack yet.", NamedTextColor.YELLOW));
}
}

Expand Down Expand Up @@ -297,13 +308,13 @@ public void onSwapHands(PlayerSwapHandItemsEvent event) {
if (!player.isSneaking() || hud.isBedrock(player)) return;
HudSession session = hud.session(player);

if (config.hud().quickHideGesture() && player.getLocation().getPitch() >= LOOK_DOWN_PITCH && player.hasPermission("bluemaphud.use")) {
if (config.hud().quickHideGesture() && player.getLocation().getPitch() >= LOOK_DOWN_PITCH && com.extantape.blueminimap.Perms.has(player, "blueminimap.use")) {
event.setCancelled(true);
session.settings().enabled = !session.settings().enabled;
hud.saveSettings(player, session);
return;
}
if (config.fullscreen().sneakSwapToggle() && config.fullscreen().enabled() && player.hasPermission("bluemaphud.fullscreen")) {
if (config.fullscreen().sneakSwapToggle() && config.fullscreen().enabled() && com.extantape.blueminimap.Perms.has(player, "blueminimap.fullscreen")) {
event.setCancelled(true);
toggleFullscreen(player);
}
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/com/extantape/blueminimap/Migration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.extantape.blueminimap;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

/** Carries a server's files over from the old plugin folder after the rename from BlueMapHUD (2026-11-09). */
final class Migration {
private Migration() {}

/**
* Copies {@code old} to {@code now} if {@code now} doesn't exist and {@code old} is a folder. Copies into a
* temporary sibling first and renames it into place, so a crash halfway leaves no half-filled folder.
*
* @return a line for the log saying what happened, or null if there was nothing to do
*/
static String copyIfMissing(Path old, Path now) {
if (Files.exists(now) || !Files.isDirectory(old)) return null;
Path tmp = now.resolveSibling(now.getFileName() + ".migrating");
try {
if (Files.exists(tmp)) deleteTree(tmp);
try (Stream<Path> all = Files.walk(old)) {
all.forEach(p -> {
Path to = tmp.resolve(old.relativize(p).toString());
try {
if (Files.isDirectory(p)) Files.createDirectories(to);
else Files.copy(p, to, StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) { throw new UncheckedIOException(e); }
});
}
Files.move(tmp, now, StandardCopyOption.ATOMIC_MOVE);
return "Copied plugins/" + old.getFileName() + " to plugins/" + now.getFileName()
+ " (BlueMapHUD is now called BlueMinimap). The old folder was left untouched; delete it once you're happy.";
} catch (IOException | UncheckedIOException e) {
return "Couldn't copy plugins/" + old.getFileName() + " to plugins/" + now.getFileName() + ": " + e.getMessage()
+ ". Copy it by hand while the server is stopped, or BlueMinimap starts with a fresh config.";
}
}

private static void deleteTree(Path root) throws IOException {
try (Stream<Path> all = Files.walk(root)) {
for (Path p : all.sorted(java.util.Comparator.reverseOrder()).toList()) Files.delete(p);
}
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/extantape/blueminimap/Perms.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.extantape.blueminimap;

import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permissible;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
* Permission checks that still respect the names from before the rename to BlueMinimap (2026-11-09), for one
* release (0.2.x): {@code bluemaphud.use} alongside {@code blueminimap.use}, and so on. The old nodes are declared
* with their old defaults, so on a server that never touched them both names agree.
*
* <p>Most nodes are on by default, so the old names matter most where a server turned one <em>off</em>, for example
* {@code bluemaphud.share.send: false} for a children's group. That must stay off after the upgrade. So for nodes
* that are on by default, a "no" on either name wins. For {@code admin}, which is for operators only by default, a
* "yes" on either name wins, so admins keep their tools. Whenever the old name is what decided the answer, that's
* logged once per player and node, saying what to change.
*/
public final class Perms {
/** Nodes that are on by default, where a deny on either name wins. Everything else: a grant on either wins. */
static final Set<String> ON_BY_DEFAULT = Set.of("blueminimap.use", "blueminimap.fullscreen", "blueminimap.seeplayers",
"blueminimap.share.send", "blueminimap.share.receive");
private static final Set<String> warned = ConcurrentHashMap.newKeySet();
private static volatile Consumer<String> warn = line -> {};

private Perms() {}

public static void logTo(Consumer<String> sink) { warn = sink; }

public static String oldName(String node) {
return "bluemaphud." + node.substring("blueminimap.".length());
}

public static boolean has(Permissible who, String node) {
boolean now = who.hasPermission(node);
if (!node.startsWith("blueminimap.")) return now;
String old = oldName(node);
boolean then = who.hasPermission(old);
if (now == then) return now;
boolean result = ON_BY_DEFAULT.contains(node) ? now && then : now || then;
if (result == now) return result; // the old name changed nothing, so there's nothing to tell anyone
String name = who instanceof CommandSender cs ? cs.getName() : String.valueOf(who);
if (warned.add(name + " " + node))
warn.accept(name + " has " + node + " " + now + " but the old " + old + " " + then + ", so it's " + result
+ ". The old name is still respected in 0.2.x but goes in the next release: set " + node + " instead.");
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.extantape.bluemaphud.api;
package com.extantape.blueminimap.api;

import org.bukkit.World;
import org.bukkit.entity.Player;
Expand All @@ -11,10 +11,10 @@
/**
* Fired on the main thread just before a player's coordinates are shared, so that another plugin, such as a
* server's chat rules, can decide who may receive them. Remove recipients to narrow the share, or cancel it
* to send nothing. Players who turned shares off, or lack {@code bluemaphud.share.receive}, are already gone.
* to send nothing. Players who turned shares off, or lack {@code blueminimap.share.receive}, are already gone.
*
* <p>When {@code share.candidates} is {@code all-online}, the recipients can include players in other
* worlds, and BlueMapHUD only does that while at least one plugin listens to this event.
* worlds, and BlueMinimap only does that while at least one plugin listens to this event.
*/
public final class CoordinateShareEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
Expand Down
Loading
Loading