diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e849c7..b448e92 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,5 +19,5 @@ jobs: - run: ./gradlew build - uses: actions/upload-artifact@v4 with: - name: BlueMapHUD + name: BlueMinimap path: build/libs/*.jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8abb530..043d5d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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" diff --git a/README.md b/README.md index 5af2651..c9505ed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# BlueMapHUD +# BlueMinimap -A minimap and coordinates HUD for Paper, drawn from [BlueMap](https://bluemap.bluecolored.de/)'s tiles, with a fullscreen map too. It uses a small resource pack with its own namespace: no client mods, no shader overrides and no held map items. +BlueMinimap: a BlueMap minimap and coordinates HUD for Paper, rendered in game from [BlueMap](https://bluemap.bluecolored.de/)'s own tiles, with a fullscreen map too. It's an add-on to BlueMap, not a separate map: it needs BlueMap on the server and shows what BlueMap has rendered. It uses a small resource pack with its own namespace: no client mods, no shader overrides and no held map items. It's an unofficial add-on for BlueMap and isn't affiliated with BlueMap's authors. @@ -19,12 +19,22 @@ These minimums come from BlueMap's release notes. Only BlueMap 5.29 was tested, ## Install -1. Put `BlueMapHUD-.jar` in `plugins/`, next to BlueMap. +1. Put `BlueMinimap-.jar` in `plugins/`, next to BlueMap. 2. Open port `8164` so players can download the pack, or set `pack.public-url` to host it elsewhere. Servers sharing a machine each need their own `pack.port`. -3. Start the server. Settings are in `plugins/BlueMapHUD/config.yml`. +3. Start the server. Settings are in `plugins/BlueMinimap/config.yml`. If BlueMap stores its maps in SQL, set `tiles.source: http`. +## Upgrading from BlueMapHUD + +BlueMinimap was called BlueMapHUD up to 0.1.0, and renamed in 0.2.0. Players keep their HUD settings. + +- With the server stopped, swap `BlueMapHUD-0.1.0.jar` for `BlueMinimap-0.2.0.jar`. +- On first start, `plugins/BlueMapHUD/` is copied to `plugins/BlueMinimap/` if that doesn't exist yet, so your config, themes and icons come across. The old folder is left untouched as a backup; nothing is overwritten. The pack is rebuilt as `plugins/BlueMinimap/pack/blueminimap-pack.zip`. If you host the pack yourself (`public-url`), upload the new file. +- For 0.2.x only, the old names still work: `/bmhud` (logged at startup), `/bluemaphud:map`, `/bluemaphud:hud` and the `bluemaphud.*` permissions. They go in the next release. For permissions that are on by default, a "no" on either the old or the new name wins, so a group you'd stopped from sharing stays stopped. Any difference between the two names is logged once per player. Set the `blueminimap.*` names instead. +- If HouseRules is installed, update it to 0.2.0 at the same time. With an older HouseRules, BlueMinimap turns `/hud share` off and logs why, because HouseRules 0.1.x wouldn't see the shares and couldn't keep them inside chat circles. +- Plugins that listen for shares: the event is now `com.extantape.blueminimap.api.CoordinateShareEvent`, with the same methods. + ## Commands | Command | What it does | @@ -42,25 +52,25 @@ If BlueMap stores its maps in SQL, set `tiles.source: http`. | `/hud hide\|show shares` | Stops or restarts receiving coordinates other players share. | | `/hud theme`, `/hud theme \|default` | Lists the themes, with yours marked and each one clickable, or switches to one. `default` goes back to the server's. The choice is saved per player. | | `/hud mode coords\|map` | `coords` shows only the X, Y, Z and facing line, in the same bar, and works in worlds BlueMap doesn't map. `map` brings the minimap back. Both turn the HUD on, and `/hud mode` alone says which one you have. New players start in `hud.default-mode`, which is `map` unless the config says `coords`. | -| `/bmhud help` | Shows help. | -| `/bmhud reload` | Reloads the config, and re-sends the pack if it changed. | -| `/bmhud debug` | Shows cache sizes, build times and update sizes. | -| `/bmhud debug dump` | Writes your view to a PNG to compare with BlueMap. | -| `/bmhud debug pattern` | Shows a test pattern. | -| `/bmhud pack export` | Shows where the pack zip is, its SHA-1, and the address players are sent. | +| `/blueminimap help` | Shows help. | +| `/blueminimap reload` | Reloads the config, and re-sends the pack if it changed. | +| `/blueminimap debug` | Shows cache sizes, build times and update sizes. | +| `/blueminimap debug dump` | Writes your view to a PNG to compare with BlueMap. | +| `/blueminimap debug pattern` | Shows a test pattern. | +| `/blueminimap pack export` | Shows where the pack zip is, its SHA-1, and the address players are sent. | -If another plugin owns a name, BlueMapHUD backs off: `/map` to `/worldmap`, then `/bmhud map`, and `/hud` to `/minimap`, then `/bmhud hud`. It logs which names it got. `/bmhud map`, `/bmhud hud`, `/bluemaphud:map` and `/bluemaphud:hud` always work. New names apply after a restart. +If another plugin owns a name, BlueMinimap backs off: `/map` to `/worldmap`, then `/blueminimap map`, and `/hud` to `/minimap`, then `/blueminimap hud`. It logs which names it got. `/blueminimap map`, `/blueminimap hud`, `/blueminimap:map` and `/blueminimap:hud` always work. New names apply after a restart. ## Permissions | Permission | Default | Allows | | --- | --- | --- | -| `bluemaphud.use` | everyone | Seeing the map at all. Removing it hides everything for that group. | -| `bluemaphud.fullscreen` | everyone | The fullscreen map. | -| `bluemaphud.seeplayers` | everyone | Other players on the map. | -| `bluemaphud.share.send` | everyone | Sharing your coordinates with `/hud share`. | -| `bluemaphud.share.receive` | everyone | Receiving coordinates other players share. | -| `bluemaphud.admin` | ops | `/bmhud reload`, `debug` and `pack`. | +| `blueminimap.use` | everyone | Seeing the map at all. Removing it hides everything for that group. | +| `blueminimap.fullscreen` | everyone | The fullscreen map. | +| `blueminimap.seeplayers` | everyone | Other players on the map. | +| `blueminimap.share.send` | everyone | Sharing your coordinates with `/hud share`. | +| `blueminimap.share.receive` | everyone | Receiving coordinates other players share. | +| `blueminimap.admin` | ops | `/blueminimap reload`, `debug` and `pack`. | ## Themes and the clock @@ -77,7 +87,7 @@ A theme sets the HUD's colours, the plate behind the coordinates and the look of The clock follows the coordinates as a small picture and 24-hour time, such as `18:30`. It changes every `hud.clock.step-minutes` in-game minutes, 5 by default, and its digits are fixed-width, so the line never shifts. It's hidden where time stands still, such as the Nether and the End. `hud.clock.enabled: false` turns it off for everyone, and `/hud hide clock` for one player. -**Your own theme.** Copy one of the files in [`src/main/resources/themes`](src/main/resources/themes) into `plugins/BlueMapHUD/themes/`, rename it (lowercase, such as `ocean.json`), change it, and run `/bmhud reload`. A file with a built-in theme's name replaces it. The file holds: +**Your own theme.** Copy one of the files in [`src/main/resources/themes`](src/main/resources/themes) into `plugins/BlueMinimap/themes/`, rename it (lowercase, such as `ocean.json`), change it, and run `/blueminimap reload`. A file with a built-in theme's name replaces it. The file holds: - `name`: the name players see. - `colors`: `text`, `facing`, `time` and `legend` for text, `plate` for the plate, and `self`, `player`, `poi`, `death` and `north` for the map's icons. Each is `#RRGGBB`. @@ -91,23 +101,23 @@ Up to 8 themes fit in the pack. Every built-in theme passes the same readability `/hud share` sends your current coordinates to people you choose, as a chat message with the coordinates and the dimension, such as `Alex shared where they are: 120 64 -35 in the Overworld`. Clicking the numbers copies them. Nothing is shared unless a player asks, and there's no teleport link. -- **Who can receive:** by default, players in the sender's world, counting its `_nether` and `_the_end` as the same world, who can see the sender, have `bluemaphud.share.receive`, and haven't turned shares off with `/hud hide shares`. +- **Who can receive:** by default, players in the sender's world, counting its `_nether` and `_the_end` as the same world, who can see the sender, have `blueminimap.share.receive`, and haven't turned shares off with `/hud hide shares`. - **Feedback:** the sender is told exactly who got their coordinates, and who couldn't. - **Limits:** a player waits `share.cooldown-seconds` (30 by default) between shares, and each share is logged to the console. `share.enabled: false` turns sharing off. -**Your server's own rules.** BlueMapHUD fires `com.extantape.bluemaphud.api.CoordinateShareEvent` on the main thread before each share. It carries the sender, the world, the block coordinates and a mutable set of recipients. A plugin can remove recipients, for example to match who may chat with whom, or cancel the share. With `share.candidates: all-online`, recipients can include every player online, so that such a plugin can follow rules that reach across worlds. BlueMapHUD only widens a share like that while a plugin is listening to the event, and logs a warning at startup if nothing is. To build against it, depend on BlueMapHUD as a server dependency with `join-classpath: true` in your `paper-plugin.yml`. +**Your server's own rules.** BlueMinimap fires `com.extantape.blueminimap.api.CoordinateShareEvent` on the main thread before each share. It carries the sender, the world, the block coordinates and a mutable set of recipients. A plugin can remove recipients, for example to match who may chat with whom, or cancel the share. With `share.candidates: all-online`, recipients can include every player online, so that such a plugin can follow rules that reach across worlds. BlueMinimap only widens a share like that while a plugin is listening to the event, and logs a warning at startup if nothing is. To build against it, depend on BlueMinimap as a server dependency with `join-classpath: true` in your `paper-plugin.yml`. ## Resource packs The plugin sends its pack alongside any others using the multi-pack API, and players don't see a map until their client confirms it loaded. With `pack.required`, a player who declines the pack is kicked with `pack.kick-message`. A download that fails never kicks: the player plays without the map and is told why, admins are warned, and the pack is offered again when they change worlds or rejoin. Its only file outside its own namespace is a transparent boss bar texture, in pink by default. If another pack restyles that colour, set `pack.boss-bar-color` to one it leaves alone. -**Using it with Magic.** Magic sends its pack with Bukkit's older one-pack method, which clears every other server pack. BlueMapHUD re-sends its pack once when that happens, but it's better to stop the conflict. In `plugins/Magic/config.yml` set `resource_pack: ""`, then add Magic's pack to your main pack. +**Using it with Magic.** Magic sends its pack with Bukkit's older one-pack method, which clears every other server pack. BlueMinimap re-sends its pack once when that happens, but it's better to stop the conflict. In `plugins/Magic/config.yml` set `resource_pack: ""`, then add Magic's pack to your main pack. -**Hosting it elsewhere.** Set `pack.public-url` to where you upload `plugins/BlueMapHUD/pack/bluemaphud-pack.zip`. Upload it unchanged, because re-zipping changes its SHA-1 and clients reject a pack whose hash doesn't match. `{sha1}` in the address is replaced with the pack's hash, such as `https://example.com/packs/{sha1}.zip`, so the address changes whenever the pack does. The same settings and plugin version always build the same hash. On startup and `/bmhud reload` the plugin downloads the address once in the background and warns, with both hashes, if it isn't the current pack. It never blocks startup. If the server can't reach the address but players can, set `pack.verify-public-url: false`. `/bmhud pack export` shows the path, the hash and the address players are sent. +**Hosting it elsewhere.** Set `pack.public-url` to where you upload `plugins/BlueMinimap/pack/blueminimap-pack.zip`. Upload it unchanged, because re-zipping changes its SHA-1 and clients reject a pack whose hash doesn't match. `{sha1}` in the address is replaced with the pack's hash, such as `https://example.com/packs/{sha1}.zip`, so the address changes whenever the pack does. The same settings and plugin version always build the same hash. On startup and `/blueminimap reload` the plugin downloads the address once in the background and warns, with both hashes, if it isn't the current pack. It never blocks startup. If the server can't reach the address but players can, set `pack.verify-public-url: false`. `/blueminimap pack export` shows the path, the hash and the address players are sent. -**Using a merged pack.** Set `pack.send: false`, run `/bmhud pack export`, and merge that zip into your server's pack. On startup the plugin warns if its files are missing. +**Using a merged pack.** Set `pack.send: false`, run `/blueminimap pack export`, and merge that zip into your server's pack. On startup the plugin warns if its files are missing. -**Custom icons.** Put 9x9 PNGs in `plugins/BlueMapHUD/icons/` and run `/bmhud reload`. The names are `arrow_00.png` to `arrow_15.png` (clockwise from north), `player.png`, `poi.png`, `death.png` and `north.png`. Draw them in white, because the plugin tints them. +**Custom icons.** Put 9x9 PNGs in `plugins/BlueMinimap/icons/` and run `/blueminimap reload`. The names are `arrow_00.png` to `arrow_15.png` (clockwise from north), `player.png`, `poi.png`, `death.png` and `north.png`. Draw them in white, because the plugin tints them. ## Known limits @@ -135,7 +145,7 @@ Measured in the test suite on a synthetic map: | Minimap update, 32x32 with 20 players, compressed | about 1.7 KB | 5 KB | | Fullscreen update, 96x54, compressed | about 4.5 KB | none | -Real terrain has more colour variation than the test map, so expect larger updates in play. `/bmhud debug` reports sizes before compression. +Real terrain has more colour variation than the test map, so expect larger updates in play. `/blueminimap debug` reports sizes before compression. ## Testing @@ -156,14 +166,14 @@ The live tests are in [`test/live`](test/live/README.md), with what they cover a - **Hiding:** vanish, `hidePlayer`, spectators, invisibility, players hidden in BlueMap, the radius, other dimensions, permissions, other boss bars, the Wither, the Ender Dragon and every `auto-hide` setting. - **Updates:** nothing is sent while idle apart from each clock step, and at most 4 updates a second otherwise. - **Pack handling:** required, optional and declined packs, failed downloads (no kick, a message, an admin warning and a retry on world change), `host`, `public-url` with and without `{sha1}`, the hosted-pack check (matching, stale, missing, unreachable, never answering, invalid and turned off), `send: false`, bar colours, custom icons, and another plugin wiping packs with the old one-pack method. -- **Life cycle:** `/bmhud reload`, `/bluemap reload`, a full `/reload`, server restarts, saved settings and quick join/quits. +- **Life cycle:** `/blueminimap reload`, `/bluemap reload`, a full `/reload`, server restarts, saved settings and quick join/quits. - **Bad data:** corrupt tiles, a wrong web root, an unreachable webserver and the far edge of the world. - **Other setups:** the action bar channel, the HTTP source, and command back-off from another plugin's `plugin.yml` commands. **Also passed:** - **SQL storage:** on 26.3, tiles held in SQLite through the HTTP source matched the same world rendered to files on all 1024 cells. -- **Startup:** BlueMap loaded on all 8 of 8 restarts of a 26.3 server with BlueMapHUD installed. +- **Startup:** BlueMap loaded on all 8 of 8 restarts of a 26.3 server with BlueMinimap installed. - **Game code:** the Minecraft 26.1.2, 26.2 and 26.3 client code loads the generated pack and fonts, with the game's advances matching this project's for all 18,944 glyphs, the clock pictures and fixed-width time digits included. The 26.1, 26.1.1 and 26.1.2 clients all use resource pack format 84, the pack's minimum. **20 moving players, one minute, on real terrain.** 26.3 is this release, with themes and the clock; 26.1.2 and 26.2 were measured on the build just before them: @@ -185,7 +195,7 @@ These ran on a shared machine with other test servers running, so tick times var - ZigZag loaded alongside the pack in a client. It overlaps only on the pink boss bar textures, which this pack replaces. - A Bedrock join, including Floodgate detection through `paper-plugin.yml`'s classpath sharing, MySQL or PostgreSQL storage, and back-off from another plugin's Brigadier commands. -To check a real client, join as an op and run `/bmhud debug pattern`. You should see a sharp colour gradient with a white border and no bar behind it. +To check a real client, join as an op and run `/blueminimap debug pattern`. You should see a sharp colour gradient with a white border and no bar behind it. ## Building diff --git a/build.gradle.kts b/build.gradle.kts index 05a1271..00eaf7d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } group = "com.extantape" -version = "0.1.0" +version = "0.2.0" java { toolchain.languageVersion.set(JavaLanguageVersion.of(25)) @@ -52,6 +52,6 @@ tasks.test { } tasks.jar { - archiveBaseName.set("BlueMapHUD") + archiveBaseName.set("BlueMinimap") from("LICENSE") } diff --git a/docs/26x-audit.md b/docs/26x-audit.md index aa03bab..8a17715 100644 --- a/docs/26x-audit.md +++ b/docs/26x-audit.md @@ -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 @@ -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`. @@ -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. diff --git a/docs/decisions.md b/docs/decisions.md index 677bac0..23e7716 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -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). diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md new file mode 100644 index 0000000..733336f --- /dev/null +++ b/docs/releases/v0.2.0.md @@ -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). diff --git a/settings.gradle.kts b/settings.gradle.kts index e513bf7..4694de7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") diff --git a/src/main/java/com/extantape/bluemaphud/BlueMapHudPlugin.java b/src/main/java/com/extantape/blueminimap/BlueMinimapPlugin.java similarity index 86% rename from src/main/java/com/extantape/bluemaphud/BlueMapHudPlugin.java rename to src/main/java/com/extantape/blueminimap/BlueMinimapPlugin.java index 8350f45..c3eedcd 100644 --- a/src/main/java/com/extantape/bluemaphud/BlueMapHudPlugin.java +++ b/src/main/java/com/extantape/blueminimap/BlueMinimapPlugin.java @@ -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; @@ -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; @@ -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")); @@ -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)); } } @@ -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); } diff --git a/src/main/java/com/extantape/blueminimap/Migration.java b/src/main/java/com/extantape/blueminimap/Migration.java new file mode 100644 index 0000000..4e5b6d4 --- /dev/null +++ b/src/main/java/com/extantape/blueminimap/Migration.java @@ -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 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 all = Files.walk(root)) { + for (Path p : all.sorted(java.util.Comparator.reverseOrder()).toList()) Files.delete(p); + } + } +} diff --git a/src/main/java/com/extantape/blueminimap/Perms.java b/src/main/java/com/extantape/blueminimap/Perms.java new file mode 100644 index 0000000..1520c19 --- /dev/null +++ b/src/main/java/com/extantape/blueminimap/Perms.java @@ -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. + * + *

Most nodes are on by default, so the old names matter most where a server turned one off, 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 ON_BY_DEFAULT = Set.of("blueminimap.use", "blueminimap.fullscreen", "blueminimap.seeplayers", + "blueminimap.share.send", "blueminimap.share.receive"); + private static final Set warned = ConcurrentHashMap.newKeySet(); + private static volatile Consumer warn = line -> {}; + + private Perms() {} + + public static void logTo(Consumer 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; + } +} diff --git a/src/main/java/com/extantape/bluemaphud/api/CoordinateShareEvent.java b/src/main/java/com/extantape/blueminimap/api/CoordinateShareEvent.java similarity index 92% rename from src/main/java/com/extantape/bluemaphud/api/CoordinateShareEvent.java rename to src/main/java/com/extantape/blueminimap/api/CoordinateShareEvent.java index 61dba0d..76dc04e 100644 --- a/src/main/java/com/extantape/bluemaphud/api/CoordinateShareEvent.java +++ b/src/main/java/com/extantape/blueminimap/api/CoordinateShareEvent.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.api; +package com.extantape.blueminimap.api; import org.bukkit.World; import org.bukkit.entity.Player; @@ -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. * *

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(); diff --git a/src/main/java/com/extantape/bluemaphud/command/HudCommands.java b/src/main/java/com/extantape/blueminimap/command/HudCommands.java similarity index 88% rename from src/main/java/com/extantape/bluemaphud/command/HudCommands.java rename to src/main/java/com/extantape/blueminimap/command/HudCommands.java index 329e658..dfbf05f 100644 --- a/src/main/java/com/extantape/bluemaphud/command/HudCommands.java +++ b/src/main/java/com/extantape/blueminimap/command/HudCommands.java @@ -1,13 +1,13 @@ -package com.extantape.bluemaphud.command; +package com.extantape.blueminimap.command; -import com.extantape.bluemaphud.BlueMapHudPlugin; -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.hud.HudManager; -import com.extantape.bluemaphud.hud.HudSession; -import com.extantape.bluemaphud.hud.PlayerSettings; -import com.extantape.bluemaphud.share.ShareService; -import com.extantape.bluemaphud.theme.Theme; -import com.extantape.bluemaphud.theme.ThemeSet; +import com.extantape.blueminimap.BlueMinimapPlugin; +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.hud.HudManager; +import com.extantape.blueminimap.hud.HudSession; +import com.extantape.blueminimap.hud.PlayerSettings; +import com.extantape.blueminimap.share.ShareService; +import com.extantape.blueminimap.theme.Theme; +import com.extantape.blueminimap.theme.ThemeSet; import com.mojang.brigadier.Command; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; @@ -30,25 +30,32 @@ import java.util.Set; import java.util.function.BiConsumer; -/** Registers /map, /hud and /bmhud, backing off to the next name when one is already taken. */ +/** Registers /map, /hud and /blueminimap, backing off to the next name when one is already taken. */ public final class HudCommands { - private final BlueMapHudPlugin plugin; + private final BlueMinimapPlugin plugin; /** Names chosen at startup. Data pack reloads rebuild the tree and must not back off from themselves. */ private String claimedMap; private String claimedHud; - public HudCommands(BlueMapHudPlugin plugin) { + public HudCommands(BlueMinimapPlugin plugin) { this.plugin = plugin; } public void register(Commands commands, HudConfig config) { - LiteralArgumentBuilder admin = Commands.literal("bmhud") + commands.register(adminTree("blueminimap").build(), "BlueMinimap help and admin commands", List.of()); + // The old name, for one release (0.2.x) after the rename from BlueMapHUD. Logged at startup. + commands.register(adminTree("bmhud").build(), "Old name for /blueminimap. Goes in the next release.", List.of()); + registerShortNames(commands, config); + } + + private LiteralArgumentBuilder adminTree(String label) { + return Commands.literal(label) .then(Commands.literal("help").executes(ctx -> help(ctx.getSource().getSender()))) .then(mapTree(Commands.literal("map"))) .then(hudTree(Commands.literal("hud"))) .then(Commands.literal("reload").requires(HudCommands::isAdmin).executes(ctx -> { plugin.reload(); - return reply(ctx.getSource().getSender(), "BlueMapHUD reloaded. Command names change after a restart."); + return reply(ctx.getSource().getSender(), "BlueMinimap reloaded. Command names change after a restart."); })) .then(Commands.literal("debug").requires(HudCommands::isAdmin) .executes(ctx -> reply(ctx.getSource().getSender(), plugin.debugLine())) @@ -59,15 +66,19 @@ public void register(Commands commands, HudConfig config) { })))) .then(Commands.literal("pack").requires(HudCommands::isAdmin) .then(Commands.literal("export").executes(ctx -> exportPack(ctx.getSource().getSender())))); - commands.register(admin.build(), "BlueMapHUD help and admin commands", List.of()); + } + private void registerShortNames(Commands commands, HudConfig config) { boolean firstTime = claimedMap == null && claimedHud == null; claimedMap = claim(commands, claimedMap != null ? List.of(claimedMap) : config.commands().mapNames(), "Open the fullscreen map", true, firstTime); claimedHud = claim(commands, claimedHud != null ? List.of(claimedHud) : config.commands().hudNames(), "Control the minimap", false, firstTime); // Namespaced names always exist, so a taken short name never locks anyone out. - addNamespaced(commands, "map", claimedMap, mapTree(Commands.literal("bluemaphud:map"))); + addNamespaced(commands, "map", claimedMap, mapTree(Commands.literal("blueminimap:map"))); + addNamespaced(commands, "hud", claimedHud, hudTree(Commands.literal("blueminimap:hud"))); + addNamespaced(commands, "map", claimedMap, mapTree(Commands.literal("bluemaphud:map"))); // old names, 0.2.x only addNamespaced(commands, "hud", claimedHud, hudTree(Commands.literal("bluemaphud:hud"))); - plugin.getLogger().info("Commands: fullscreen map is " + describe(claimedMap, "/bmhud map") + ", minimap is " + describe(claimedHud, "/bmhud hud") + "."); + plugin.getLogger().info("/bmhud, /bluemaphud:map and /bluemaphud:hud are the old names for /blueminimap, /blueminimap:map and /blueminimap:hud. They still work in 0.2.x but go in the next release."); + plugin.getLogger().info("Commands: fullscreen map is " + describe(claimedMap, "/blueminimap map") + ", minimap is " + describe(claimedHud, "/blueminimap hud") + "."); } private String claim(Commands commands, List names, String description, boolean isMap, boolean checkLegacy) { @@ -96,11 +107,11 @@ private static String describe(String claimed, String fallback) { } private static boolean isAdmin(CommandSourceStack source) { - return source.getSender().hasPermission("bluemaphud.admin"); + return com.extantape.blueminimap.Perms.has(source.getSender(), "blueminimap.admin"); } private LiteralArgumentBuilder mapTree(LiteralArgumentBuilder root) { - return root.requires(source -> source.getSender().hasPermission("bluemaphud.fullscreen")) + return root.requires(source -> com.extantape.blueminimap.Perms.has(source.getSender(), "blueminimap.fullscreen")) .executes(player((p, s) -> plugin.toggleFullscreen(p))) .then(Commands.literal("zoom").then(Commands.argument("level", StringArgumentType.word()) .suggests((ctx, builder) -> suggestZoom(builder)) @@ -117,7 +128,7 @@ private LiteralArgumentBuilder mapTree(LiteralArgumentBuilde } private LiteralArgumentBuilder hudTree(LiteralArgumentBuilder root) { - return root.requires(source -> source.getSender().hasPermission("bluemaphud.use")) + return root.requires(source -> com.extantape.blueminimap.Perms.has(source.getSender(), "blueminimap.use")) .executes(player((p, s) -> setEnabled(p, s, !s.settings().enabled))) .then(Commands.literal("on").executes(player((p, s) -> setEnabled(p, s, true)))) .then(Commands.literal("off").executes(player((p, s) -> setEnabled(p, s, false)))) @@ -254,7 +265,7 @@ private void listThemes(Player player, HudSession session) { boolean mine = i == current; Component name = Component.text(theme.name(), mine ? NamedTextColor.WHITE : NamedTextColor.AQUA) .decoration(TextDecoration.UNDERLINED, !mine) - .clickEvent(ClickEvent.runCommand("/bluemaphud:hud theme " + theme.id())) + .clickEvent(ClickEvent.runCommand("/blueminimap:hud theme " + theme.id())) .hoverEvent(HoverEvent.showText(Component.text(mine ? "Your theme" : "Switch to " + theme.name()))); line = line.append(name).append(Component.text(mine ? " (yours)" : "", NamedTextColor.GRAY)) .append(Component.text(i + 1 < themes.size() ? ", " : "", NamedTextColor.GRAY)); @@ -348,12 +359,12 @@ private int withPlayer(CommandContext ctx, BiConsumer. Sneak + F also opens it.")); sender.sendMessage(Component.text("Minimap: on, off, mode map or coords, zoom, size, offset, corner, theme, hide and show (coords, players, markers, clock, shares).")); sender.sendMessage(Component.text("Share your coordinates: /hud share, /hud share or /hud share everyone.")); - if (sender.hasPermission("bluemaphud.admin")) { - sender.sendMessage(Component.text("Admin: /bmhud reload, debug, debug dump, debug pattern, pack export.")); + if (com.extantape.blueminimap.Perms.has(sender, "blueminimap.admin")) { + sender.sendMessage(Component.text("Admin: /blueminimap reload, debug, debug dump, debug pattern, pack export.")); sender.sendMessage(Component.text(hud.debugLine())); } return Command.SINGLE_SUCCESS; diff --git a/src/main/java/com/extantape/bluemaphud/config/HudConfig.java b/src/main/java/com/extantape/blueminimap/config/HudConfig.java similarity index 99% rename from src/main/java/com/extantape/bluemaphud/config/HudConfig.java rename to src/main/java/com/extantape/blueminimap/config/HudConfig.java index d6e86e6..e9e3e43 100644 --- a/src/main/java/com/extantape/bluemaphud/config/HudConfig.java +++ b/src/main/java/com/extantape/blueminimap/config/HudConfig.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.config; +package com.extantape.blueminimap.config; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; @@ -121,7 +121,7 @@ public static HudConfig from(FileConfiguration c) { Pack pack = new Pack( c.getBoolean("pack.send", true), c.getBoolean("pack.required", true), - c.getString("pack.kick-message", "This server needs the BlueMapHUD resource pack."), + c.getString("pack.kick-message", "This server needs the BlueMinimap resource pack."), c.getString("pack.prompt", ""), c.getInt("pack.port", 8164), c.getString("pack.host", ""), diff --git a/src/main/java/com/extantape/bluemaphud/hud/FrameBuilder.java b/src/main/java/com/extantape/blueminimap/hud/FrameBuilder.java similarity index 94% rename from src/main/java/com/extantape/bluemaphud/hud/FrameBuilder.java rename to src/main/java/com/extantape/blueminimap/hud/FrameBuilder.java index da2e31b..419afd5 100644 --- a/src/main/java/com/extantape/bluemaphud/hud/FrameBuilder.java +++ b/src/main/java/com/extantape/blueminimap/hud/FrameBuilder.java @@ -1,16 +1,16 @@ -package com.extantape.bluemaphud.hud; - -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.markers.MarkerService; -import com.extantape.bluemaphud.render.FontLayout; -import com.extantape.bluemaphud.render.HudFonts; -import com.extantape.bluemaphud.render.HudIcon; -import com.extantape.bluemaphud.render.MapComposer; -import com.extantape.bluemaphud.render.MapFrame; -import com.extantape.bluemaphud.render.ReadoutFont; -import com.extantape.bluemaphud.render.TinyFont; -import com.extantape.bluemaphud.theme.Theme; -import com.extantape.bluemaphud.theme.ThemeSet; +package com.extantape.blueminimap.hud; + +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.markers.MarkerService; +import com.extantape.blueminimap.render.FontLayout; +import com.extantape.blueminimap.render.HudFonts; +import com.extantape.blueminimap.render.HudIcon; +import com.extantape.blueminimap.render.MapComposer; +import com.extantape.blueminimap.render.MapFrame; +import com.extantape.blueminimap.render.ReadoutFont; +import com.extantape.blueminimap.render.TinyFont; +import com.extantape.blueminimap.theme.Theme; +import com.extantape.blueminimap.theme.ThemeSet; import net.kyori.adventure.text.Component; import java.util.Arrays; diff --git a/src/main/java/com/extantape/bluemaphud/hud/HudManager.java b/src/main/java/com/extantape/blueminimap/hud/HudManager.java similarity index 96% rename from src/main/java/com/extantape/bluemaphud/hud/HudManager.java rename to src/main/java/com/extantape/blueminimap/hud/HudManager.java index f8deccf..b58e209 100644 --- a/src/main/java/com/extantape/bluemaphud/hud/HudManager.java +++ b/src/main/java/com/extantape/blueminimap/hud/HudManager.java @@ -1,7 +1,7 @@ -package com.extantape.bluemaphud.hud; +package com.extantape.blueminimap.hud; -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.pack.PackService; +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.pack.PackService; import de.bluecolored.bluemap.api.BlueMapAPI; import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.BlueMapWorld; @@ -46,7 +46,7 @@ public final class HudManager { private final Map worldMaps = new HashMap<>(); private final Map bedrock = new ConcurrentHashMap<>(); private final ExecutorService builders = Executors.newFixedThreadPool(2, runnable -> { - Thread thread = new Thread(runnable, "BlueMapHUD-build"); + Thread thread = new Thread(runnable, "BlueMinimap-build"); thread.setDaemon(true); thread.setPriority(Thread.NORM_PRIORITY - 1); return thread; @@ -183,7 +183,7 @@ private void tick() { String mapId = mapFor(player.getWorld()); boolean hasMap = !mapId.isEmpty() || session.pattern; - boolean canFull = hasMap && session.fullscreen && player.hasPermission("bluemaphud.fullscreen") && config.fullscreen().enabled(); + boolean canFull = hasMap && session.fullscreen && com.extantape.blueminimap.Perms.has(player, "blueminimap.fullscreen") && config.fullscreen().enabled(); if (!shouldShow(player, session, hasMap, canFull)) { hide(player, session); continue; @@ -200,7 +200,7 @@ private boolean shouldShow(Player player, HudSession session, boolean hasMap, bo boolean coordsOnly = session.settings.coordsOnly && !session.pattern; if (!hasMap && !coordsOnly) return false; // A dimension BlueMap doesn't map. Coordinates don't need one. if (isBedrock(player) || !packs.ready(player)) return false; - if (!player.hasPermission("bluemaphud.use")) return false; + if (!com.extantape.blueminimap.Perms.has(player, "blueminimap.use")) return false; if (canFull) return true; // Fullscreen is an explicit request, and it works while the minimap is off. if (!session.settings.enabled) return false; if (session.fullscreen && config.hud().hideWhileFullscreen() && !coordsOnly) return false; @@ -253,7 +253,7 @@ private int clockMinutes(World world, PlayerSettings settings) { } private int[] visiblePlayers(Player viewer, HudSession session, Location at) { - if (!session.settings.showPlayers || !viewer.hasPermission("bluemaphud.seeplayers")) return new int[0]; + if (!session.settings.showPlayers || !com.extantape.blueminimap.Perms.has(viewer, "blueminimap.seeplayers")) return new int[0]; HudConfig.Players rules = config.players(); BlueMapAPI api = rules.hideBlueMapHidden() ? BlueMapAPI.getInstance().orElse(null) : null; double radiusSquared = (double) rules.radius() * rules.radius(); diff --git a/src/main/java/com/extantape/bluemaphud/hud/HudSession.java b/src/main/java/com/extantape/blueminimap/hud/HudSession.java similarity index 96% rename from src/main/java/com/extantape/bluemaphud/hud/HudSession.java rename to src/main/java/com/extantape/blueminimap/hud/HudSession.java index 18fbde2..600e6fe 100644 --- a/src/main/java/com/extantape/bluemaphud/hud/HudSession.java +++ b/src/main/java/com/extantape/blueminimap/hud/HudSession.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.hud; +package com.extantape.blueminimap.hud; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; diff --git a/src/main/java/com/extantape/bluemaphud/hud/PlayerSettings.java b/src/main/java/com/extantape/blueminimap/hud/PlayerSettings.java similarity index 97% rename from src/main/java/com/extantape/bluemaphud/hud/PlayerSettings.java rename to src/main/java/com/extantape/blueminimap/hud/PlayerSettings.java index 9bc303b..d44577d 100644 --- a/src/main/java/com/extantape/bluemaphud/hud/PlayerSettings.java +++ b/src/main/java/com/extantape/blueminimap/hud/PlayerSettings.java @@ -1,6 +1,6 @@ -package com.extantape.bluemaphud.hud; +package com.extantape.blueminimap.hud; -import com.extantape.bluemaphud.config.HudConfig; +import com.extantape.blueminimap.config.HudConfig; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; diff --git a/src/main/java/com/extantape/bluemaphud/hud/Snapshot.java b/src/main/java/com/extantape/blueminimap/hud/Snapshot.java similarity index 97% rename from src/main/java/com/extantape/bluemaphud/hud/Snapshot.java rename to src/main/java/com/extantape/blueminimap/hud/Snapshot.java index e34d6cb..d293198 100644 --- a/src/main/java/com/extantape/bluemaphud/hud/Snapshot.java +++ b/src/main/java/com/extantape/blueminimap/hud/Snapshot.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.hud; +package com.extantape.blueminimap.hud; /** * Everything a frame needs, copied on the main thread so that background threads never touch Bukkit. diff --git a/src/main/java/com/extantape/bluemaphud/markers/MarkerService.java b/src/main/java/com/extantape/blueminimap/markers/MarkerService.java similarity index 95% rename from src/main/java/com/extantape/bluemaphud/markers/MarkerService.java rename to src/main/java/com/extantape/blueminimap/markers/MarkerService.java index f4d1ed2..57aee57 100644 --- a/src/main/java/com/extantape/bluemaphud/markers/MarkerService.java +++ b/src/main/java/com/extantape/blueminimap/markers/MarkerService.java @@ -1,6 +1,6 @@ -package com.extantape.bluemaphud.markers; +package com.extantape.blueminimap.markers; -import com.extantape.bluemaphud.config.HudConfig; +import com.extantape.blueminimap.config.HudConfig; import de.bluecolored.bluemap.api.BlueMapAPI; import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.markers.Marker; diff --git a/src/main/java/com/extantape/bluemaphud/pack/PackBuilder.java b/src/main/java/com/extantape/blueminimap/pack/PackBuilder.java similarity index 90% rename from src/main/java/com/extantape/bluemaphud/pack/PackBuilder.java rename to src/main/java/com/extantape/blueminimap/pack/PackBuilder.java index e3433a4..e681c1a 100644 --- a/src/main/java/com/extantape/bluemaphud/pack/PackBuilder.java +++ b/src/main/java/com/extantape/blueminimap/pack/PackBuilder.java @@ -1,14 +1,14 @@ -package com.extantape.bluemaphud.pack; +package com.extantape.blueminimap.pack; -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.render.FontLayout; -import com.extantape.bluemaphud.render.HudFonts; -import com.extantape.bluemaphud.render.HudIcon; -import com.extantape.bluemaphud.render.IconSet; -import com.extantape.bluemaphud.render.ReadoutFont; -import com.extantape.bluemaphud.render.TinyFont; -import com.extantape.bluemaphud.theme.Theme; -import com.extantape.bluemaphud.theme.ThemeSet; +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.render.FontLayout; +import com.extantape.blueminimap.render.HudFonts; +import com.extantape.blueminimap.render.HudIcon; +import com.extantape.blueminimap.render.IconSet; +import com.extantape.blueminimap.render.ReadoutFont; +import com.extantape.blueminimap.render.TinyFont; +import com.extantape.blueminimap.theme.Theme; +import com.extantape.blueminimap.theme.ThemeSet; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; @@ -28,7 +28,7 @@ import java.util.zip.ZipOutputStream; /** - * Generates the resource pack in memory. Everything lives under the bluemaphud namespace, apart from + * Generates the resource pack in memory. Everything lives under the blueminimap namespace, apart from * the optional transparent boss bar textures. No shaders are involved. */ public final class PackBuilder { @@ -67,7 +67,7 @@ public static Built build(HudConfig config, HudFonts fonts, IconSet icons) throw private static byte[] packMeta() { JsonObject pack = new JsonObject(); - pack.addProperty("description", "BlueMapHUD minimap"); + pack.addProperty("description", "BlueMinimap minimap"); pack.addProperty("min_format", MIN_FORMAT); pack.addProperty("max_format", MAX_FORMAT); JsonObject root = new JsonObject(); @@ -81,18 +81,18 @@ private static void addLayout(Map files, FontLayout layout, Icon int iconCells = FontLayout.paddedCells(layout.maxIconAscent(), 1, HudIcon.SIZE); int textCells = FontLayout.paddedCells(layout.maxTextAscent(), 1, TinyFont.HEIGHT); - files.put("assets/bluemaphud/textures/font/" + name + "_runs.png", png(runSheet(runCells))); - files.put("assets/bluemaphud/textures/font/" + name + "_icons.png", png(iconSheet(icons, iconCells))); - files.put("assets/bluemaphud/textures/font/" + name + "_text.png", png(textSheet(textCells))); + files.put("assets/blueminimap/textures/font/" + name + "_runs.png", png(runSheet(runCells))); + files.put("assets/blueminimap/textures/font/" + name + "_icons.png", png(iconSheet(icons, iconCells))); + files.put("assets/blueminimap/textures/font/" + name + "_text.png", png(textSheet(textCells))); int readoutCells = FontLayout.paddedCells(layout.maxReadoutAscent(), 1, ReadoutFont.HEIGHT); int plateCells = FontLayout.paddedCells(layout.maxPlateAscent(), 1, FontLayout.PLATE_HEIGHT); - files.put("assets/bluemaphud/textures/font/" + name + "_readout.png", png(readoutSheet(readoutCells))); + files.put("assets/blueminimap/textures/font/" + name + "_readout.png", png(readoutSheet(readoutCells))); // One plate sheet for each opacity the themes use. The colour comes from the frame. for (int v = 0; v < themes.plateOpacities().size(); v++) { - files.put("assets/bluemaphud/textures/font/" + name + "_plate" + v + ".png", png(plateSheet(plateCells, themes.plateOpacities().get(v)))); + files.put("assets/blueminimap/textures/font/" + name + "_plate" + v + ".png", png(plateSheet(plateCells, themes.plateOpacities().get(v)))); } int clockCells = FontLayout.paddedCells(layout.maxClockAscent(), 1, Theme.CLOCK_SIZE); - files.put("assets/bluemaphud/textures/font/" + name + "_clock.png", png(clockSheet(themes, clockCells))); + files.put("assets/blueminimap/textures/font/" + name + "_clock.png", png(clockSheet(themes, clockCells))); JsonArray providers = new JsonArray(); providers.add(spaceProvider()); @@ -123,7 +123,7 @@ private static void addLayout(Map files, FontLayout layout, Icon } JsonObject font = new JsonObject(); font.add("providers", providers); - files.put("assets/bluemaphud/font/" + name + ".json", GSON.toJson(font).getBytes(StandardCharsets.UTF_8)); + files.put("assets/blueminimap/font/" + name + ".json", GSON.toJson(font).getBytes(StandardCharsets.UTF_8)); } private static JsonObject spaceProvider() { @@ -141,7 +141,7 @@ private static JsonObject spaceProvider() { private static JsonObject bitmap(String file, int height, int ascent, String[] chars) { JsonObject provider = new JsonObject(); provider.addProperty("type", "bitmap"); - provider.addProperty("file", "bluemaphud:font/" + file + ".png"); + provider.addProperty("file", "blueminimap:font/" + file + ".png"); provider.addProperty("height", height); provider.addProperty("ascent", ascent); JsonArray rows = new JsonArray(); diff --git a/src/main/java/com/extantape/bluemaphud/pack/PackService.java b/src/main/java/com/extantape/blueminimap/pack/PackService.java similarity index 92% rename from src/main/java/com/extantape/bluemaphud/pack/PackService.java rename to src/main/java/com/extantape/blueminimap/pack/PackService.java index f35af2f..3406021 100644 --- a/src/main/java/com/extantape/bluemaphud/pack/PackService.java +++ b/src/main/java/com/extantape/blueminimap/pack/PackService.java @@ -1,6 +1,6 @@ -package com.extantape.bluemaphud.pack; +package com.extantape.blueminimap.pack; -import com.extantape.bluemaphud.config.HudConfig; +import com.extantape.blueminimap.config.HudConfig; import com.sun.net.httpserver.HttpServer; import net.kyori.adventure.resource.ResourcePackInfo; import net.kyori.adventure.resource.ResourcePackRequest; @@ -66,7 +66,7 @@ public boolean install(HudConfig.Pack config, PackBuilder.Built built) throws IO this.pack = built; Files.createDirectories(packFolder); Files.write(exportPath(), built.zip()); - servedPath = "/bluemaphud-" + built.sha1Hex() + ".zip"; + servedPath = "/blueminimap-" + built.sha1Hex() + ".zip"; servedBytes = built.zip(); if (config.send() && config.publicUrl().isBlank()) startServer(config.port()); else stopServer(); @@ -82,13 +82,13 @@ public String publicUrl() { return PublicUrlCheck.fill(config.publicUrl(), pack.sha1Hex()); } - /** The last public-url check's result, for /bmhud debug. */ + /** The last public-url check's result, for /blueminimap debug. */ public String publicUrlStatus() { return publicUrlStatus; } public Path exportPath() { - return packFolder.resolve("bluemaphud-pack.zip"); + return packFolder.resolve("blueminimap-pack.zip"); } public String sha1() { @@ -117,15 +117,15 @@ private void reportFailure(Player player, PlayerResourcePackStatusEvent.Status s + "It'll try again when you change worlds or rejoin.", NamedTextColor.YELLOW)); String reason = status == PlayerResourcePackStatusEvent.Status.INVALID_URL ? "the address was invalid" : status == PlayerResourcePackStatusEvent.Status.FAILED_RELOAD ? "it didn't load" : "the download failed"; - plugin.getLogger().warning(player.getName() + " didn't get the BlueMapHUD pack: " + reason + ". They're playing without the map." - + " Check the pack's hosting; /bmhud pack export shows the address players are sent."); + plugin.getLogger().warning(player.getName() + " didn't get the BlueMinimap pack: " + reason + ". They're playing without the map." + + " Check the pack's hosting; /blueminimap pack export shows the address players are sent."); long now = System.currentTimeMillis(); if (now - lastAdminWarning < ADMIN_WARNING_GAP_MILLIS) return; lastAdminWarning = now; - Component warning = Component.text("[BlueMapHUD] " + player.getName() + " couldn't download the map's pack (" + reason - + "). Check the pack's hosting. /bmhud pack export shows the address.", NamedTextColor.GOLD); + Component warning = Component.text("[BlueMinimap] " + player.getName() + " couldn't download the map's pack (" + reason + + "). Check the pack's hosting. /blueminimap pack export shows the address.", NamedTextColor.GOLD); for (Player admin : Bukkit.getOnlinePlayers()) { - if (admin.hasPermission("bluemaphud.admin")) admin.sendMessage(warning); + if (com.extantape.blueminimap.Perms.has(admin, "blueminimap.admin")) admin.sendMessage(warning); } } @@ -205,7 +205,7 @@ private void startServer(int port) throws IOException { return; } server.setExecutor(Executors.newFixedThreadPool(2, runnable -> { - Thread thread = new Thread(runnable, "BlueMapHUD-pack"); + Thread thread = new Thread(runnable, "BlueMinimap-pack"); thread.setDaemon(true); return thread; })); @@ -282,14 +282,14 @@ private void checkMergedPackAsync() { boolean found = false; try (ZipInputStream zip = new ZipInputStream(response.body())) { for (ZipEntry entry; (entry = zip.getNextEntry()) != null; ) { - if (entry.getName().equals("assets/bluemaphud/font/hud.json")) { + if (entry.getName().equals("assets/blueminimap/font/hud.json")) { found = true; break; } } } if (!found) { - plugin.getLogger().warning("The server's resource pack doesn't contain BlueMapHUD's files. Merge " + exportPath() + " into it."); + plugin.getLogger().warning("The server's resource pack doesn't contain BlueMinimap's files. Merge " + exportPath() + " into it."); } } catch (IOException | RuntimeException e) { plugin.getLogger().warning("Could not check the server's resource pack: " + e.getMessage()); diff --git a/src/main/java/com/extantape/bluemaphud/pack/PublicUrlCheck.java b/src/main/java/com/extantape/blueminimap/pack/PublicUrlCheck.java similarity index 96% rename from src/main/java/com/extantape/bluemaphud/pack/PublicUrlCheck.java rename to src/main/java/com/extantape/blueminimap/pack/PublicUrlCheck.java index a4faf5f..f9e8839 100644 --- a/src/main/java/com/extantape/bluemaphud/pack/PublicUrlCheck.java +++ b/src/main/java/com/extantape/blueminimap/pack/PublicUrlCheck.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.pack; +package com.extantape.blueminimap.pack; import java.io.IOException; import java.io.InputStream; @@ -48,7 +48,7 @@ public static Result check(String url, String expectedSha1, Duration timeout) { } return new Result(false, "The file at pack.public-url doesn't match the pack this plugin built. Hosted SHA-1 " + actual + ", built " + expectedSha1 + ". Players will fail the download" + kickNote() - + ". Upload plugins/BlueMapHUD/pack/bluemaphud-pack.zip unchanged to " + url + "."); + + ". Upload plugins/BlueMinimap/pack/blueminimap-pack.zip unchanged to " + url + "."); } } catch (IllegalArgumentException e) { return new Result(false, "pack.public-url isn't a valid address: " + url); diff --git a/src/main/java/com/extantape/bluemaphud/render/FontLayout.java b/src/main/java/com/extantape/blueminimap/render/FontLayout.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/render/FontLayout.java rename to src/main/java/com/extantape/blueminimap/render/FontLayout.java index ed4d4ec..a402ab3 100644 --- a/src/main/java/com/extantape/bluemaphud/render/FontLayout.java +++ b/src/main/java/com/extantape/blueminimap/render/FontLayout.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; import net.kyori.adventure.key.Key; @@ -61,7 +61,7 @@ public FontLayout(String name, int layoutIndex, int step, int rows, int reserved public FontLayout(String name, int layoutIndex, int step, int rows, int reservedRows, Direction direction, int plateVariants, int clockGlyphs, int onlyReadoutRow) { this.name = name; - this.font = Key.key("bluemaphud", name); + this.font = Key.key("blueminimap", name); this.layoutIndex = layoutIndex; this.step = step; this.rows = rows; diff --git a/src/main/java/com/extantape/bluemaphud/render/HudFonts.java b/src/main/java/com/extantape/blueminimap/render/HudFonts.java similarity index 95% rename from src/main/java/com/extantape/bluemaphud/render/HudFonts.java rename to src/main/java/com/extantape/blueminimap/render/HudFonts.java index ac62296..cc5e09f 100644 --- a/src/main/java/com/extantape/bluemaphud/render/HudFonts.java +++ b/src/main/java/com/extantape/blueminimap/render/HudFonts.java @@ -1,7 +1,7 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.theme.ThemeSet; +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.theme.ThemeSet; /** The fonts a given config needs. The pack is generated from the same object that draws the map. */ public final class HudFonts { diff --git a/src/main/java/com/extantape/bluemaphud/render/HudIcon.java b/src/main/java/com/extantape/blueminimap/render/HudIcon.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/render/HudIcon.java rename to src/main/java/com/extantape/blueminimap/render/HudIcon.java index c9d95de..becf3e6 100644 --- a/src/main/java/com/extantape/bluemaphud/render/HudIcon.java +++ b/src/main/java/com/extantape/blueminimap/render/HudIcon.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; import java.awt.image.BufferedImage; diff --git a/src/main/java/com/extantape/bluemaphud/render/IconSet.java b/src/main/java/com/extantape/blueminimap/render/IconSet.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/render/IconSet.java rename to src/main/java/com/extantape/blueminimap/render/IconSet.java index 8461938..d2a519d 100644 --- a/src/main/java/com/extantape/bluemaphud/render/IconSet.java +++ b/src/main/java/com/extantape/blueminimap/render/IconSet.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; diff --git a/src/main/java/com/extantape/bluemaphud/render/MapComposer.java b/src/main/java/com/extantape/blueminimap/render/MapComposer.java similarity index 96% rename from src/main/java/com/extantape/bluemaphud/render/MapComposer.java rename to src/main/java/com/extantape/blueminimap/render/MapComposer.java index 69f7fd6..4c55fec 100644 --- a/src/main/java/com/extantape/bluemaphud/render/MapComposer.java +++ b/src/main/java/com/extantape/blueminimap/render/MapComposer.java @@ -1,6 +1,6 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; -import com.extantape.bluemaphud.tiles.GridCache; +import com.extantape.blueminimap.tiles.GridCache; /** Fills a colour grid from cached chunks, and fingerprints those chunks to detect tile changes. */ public final class MapComposer { diff --git a/src/main/java/com/extantape/bluemaphud/render/MapFrame.java b/src/main/java/com/extantape/blueminimap/render/MapFrame.java similarity index 99% rename from src/main/java/com/extantape/bluemaphud/render/MapFrame.java rename to src/main/java/com/extantape/blueminimap/render/MapFrame.java index 9cbdca1..e9b9ef7 100644 --- a/src/main/java/com/extantape/bluemaphud/render/MapFrame.java +++ b/src/main/java/com/extantape/blueminimap/render/MapFrame.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; diff --git a/src/main/java/com/extantape/bluemaphud/render/ReadoutFont.java b/src/main/java/com/extantape/blueminimap/render/ReadoutFont.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/render/ReadoutFont.java rename to src/main/java/com/extantape/blueminimap/render/ReadoutFont.java index e20e3d0..d48ac23 100644 --- a/src/main/java/com/extantape/bluemaphud/render/ReadoutFont.java +++ b/src/main/java/com/extantape/blueminimap/render/ReadoutFont.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; /** * The 4x7 pixel font for the coordinate readout. It only has the characters coordinates need, and it is diff --git a/src/main/java/com/extantape/bluemaphud/render/TinyFont.java b/src/main/java/com/extantape/blueminimap/render/TinyFont.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/render/TinyFont.java rename to src/main/java/com/extantape/blueminimap/render/TinyFont.java index 6380995..3b7918b 100644 --- a/src/main/java/com/extantape/bluemaphud/render/TinyFont.java +++ b/src/main/java/com/extantape/blueminimap/render/TinyFont.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.render; +package com.extantape.blueminimap.render; /** * A 3x5 pixel font drawn into the pack. The HUD must know every glyph's exact advance to keep the map diff --git a/src/main/java/com/extantape/bluemaphud/share/ShareMenu.java b/src/main/java/com/extantape/blueminimap/share/ShareMenu.java similarity index 97% rename from src/main/java/com/extantape/bluemaphud/share/ShareMenu.java rename to src/main/java/com/extantape/blueminimap/share/ShareMenu.java index ab7e9fc..67bdccb 100644 --- a/src/main/java/com/extantape/bluemaphud/share/ShareMenu.java +++ b/src/main/java/com/extantape/blueminimap/share/ShareMenu.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.share; +package com.extantape.blueminimap.share; import io.papermc.paper.dialog.Dialog; import io.papermc.paper.dialog.DialogResponseView; @@ -120,9 +120,9 @@ private void onMainThread(Runnable task) { /** One line per person to click, and one for everyone. */ private static void chatList(Player sender, List candidates) { sender.sendMessage(Component.text("Share your coordinates with:", NamedTextColor.GRAY)); - sender.sendMessage(link("Everyone here (" + candidates.size() + ")", "/bluemaphud:hud share everyone")); + sender.sendMessage(link("Everyone here (" + candidates.size() + ")", "/blueminimap:hud share everyone")); for (Player player : candidates.subList(0, Math.min(MAX_LISTED, candidates.size()))) { - sender.sendMessage(link(player.getName(), "/bluemaphud:hud share " + player.getName())); + sender.sendMessage(link(player.getName(), "/blueminimap:hud share " + player.getName())); } } diff --git a/src/main/java/com/extantape/bluemaphud/share/ShareService.java b/src/main/java/com/extantape/blueminimap/share/ShareService.java similarity index 79% rename from src/main/java/com/extantape/bluemaphud/share/ShareService.java rename to src/main/java/com/extantape/blueminimap/share/ShareService.java index 2c99f2d..568ad23 100644 --- a/src/main/java/com/extantape/bluemaphud/share/ShareService.java +++ b/src/main/java/com/extantape/blueminimap/share/ShareService.java @@ -1,8 +1,8 @@ -package com.extantape.bluemaphud.share; +package com.extantape.blueminimap.share; -import com.extantape.bluemaphud.BlueMapHudPlugin; -import com.extantape.bluemaphud.api.CoordinateShareEvent; -import com.extantape.bluemaphud.config.HudConfig; +import com.extantape.blueminimap.BlueMinimapPlugin; +import com.extantape.blueminimap.api.CoordinateShareEvent; +import com.extantape.blueminimap.config.HudConfig; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; @@ -28,14 +28,14 @@ * players can turn receiving off. Other plugins can narrow or stop any share through {@link CoordinateShareEvent}. */ public final class ShareService { - public static final String SEND = "bluemaphud.share.send"; - public static final String RECEIVE = "bluemaphud.share.receive"; + public static final String SEND = "blueminimap.share.send"; + public static final String RECEIVE = "blueminimap.share.receive"; - private final BlueMapHudPlugin plugin; + private final BlueMinimapPlugin plugin; /** When each player last shared. Kept across rejoins, so leaving can't skip the cooldown. */ private final Map lastShare = new HashMap<>(); - public ShareService(BlueMapHudPlugin plugin) { + public ShareService(BlueMinimapPlugin plugin) { this.plugin = plugin; } @@ -67,7 +67,7 @@ public List candidates(Player sender) { List result = new ArrayList<>(); for (Player other : Bukkit.getOnlinePlayers()) { if (other.equals(sender) || !sender.canSee(other)) continue; - if (!other.hasPermission(RECEIVE) || !plugin.hud().session(other).settings().receiveShares) continue; + if (!com.extantape.blueminimap.Perms.has(other, RECEIVE) || !plugin.hud().session(other).settings().receiveShares) continue; if (!anyWorld && !worldGroup(other.getWorld().getName()).equals(group)) continue; result.add(other); } @@ -86,7 +86,12 @@ public static Player findVisible(Player sender, String name) { /** Why the sender can't share right now, or null if they can. */ public String blocked(Player sender) { if (!config().enabled()) return "Sharing coordinates is turned off on this server."; - if (!sender.hasPermission(SEND)) return "You can't share your coordinates here."; + if (!com.extantape.blueminimap.Perms.has(sender, SEND)) return "You can't share your coordinates here."; + String guard = olderHouseRules(); + if (guard != null) { + plugin.getLogger().warning(guard); + return "Sharing coordinates is off on this server for now. An admin has been told why."; + } Long last = lastShare.get(sender.getUniqueId()); long wait = last == null ? 0 : last + config().cooldownSeconds() * 1000L - System.currentTimeMillis(); if (wait > 0) { @@ -178,4 +183,25 @@ private static String place(World world) { default -> world.getName(); }; } + + /** + * HouseRules 0.1.x listens for the share event under BlueMapHUD's old name, so it wouldn't see shares from + * BlueMinimap and they'd skip its chat circles. Fail closed: no shares until HouseRules is 0.2 or newer. + */ + static String olderHouseRules(String version) { + if (version == null) return null; + String[] p = version.split("[.-]"); + try { + int major = Integer.parseInt(p[0]), minor = p.length > 1 ? Integer.parseInt(p[1]) : 0; + if (major > 0 || minor >= 2) return null; + } catch (NumberFormatException e) { + // Unreadable version: treat it as too old. + } + return "Coordinate sharing is off because HouseRules " + version + " can't see BlueMinimap's shares, so they'd skip its chat circles. Update HouseRules to 0.2.0 or newer."; + } + + private String olderHouseRules() { + org.bukkit.plugin.Plugin houseRules = plugin.getServer().getPluginManager().getPlugin("HouseRules"); + return houseRules == null ? null : olderHouseRules(houseRules.getPluginMeta().getVersion()); + } } diff --git a/src/main/java/com/extantape/bluemaphud/theme/Theme.java b/src/main/java/com/extantape/blueminimap/theme/Theme.java similarity index 99% rename from src/main/java/com/extantape/bluemaphud/theme/Theme.java rename to src/main/java/com/extantape/blueminimap/theme/Theme.java index 49cf4e7..77372c6 100644 --- a/src/main/java/com/extantape/bluemaphud/theme/Theme.java +++ b/src/main/java/com/extantape/blueminimap/theme/Theme.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.theme; +package com.extantape.blueminimap.theme; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/src/main/java/com/extantape/bluemaphud/theme/ThemeContrast.java b/src/main/java/com/extantape/blueminimap/theme/ThemeContrast.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/theme/ThemeContrast.java rename to src/main/java/com/extantape/blueminimap/theme/ThemeContrast.java index b777a37..2dcaf1b 100644 --- a/src/main/java/com/extantape/bluemaphud/theme/ThemeContrast.java +++ b/src/main/java/com/extantape/blueminimap/theme/ThemeContrast.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.theme; +package com.extantape.blueminimap.theme; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/extantape/bluemaphud/theme/ThemeSet.java b/src/main/java/com/extantape/blueminimap/theme/ThemeSet.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/theme/ThemeSet.java rename to src/main/java/com/extantape/blueminimap/theme/ThemeSet.java index dc7c8be..974da68 100644 --- a/src/main/java/com/extantape/bluemaphud/theme/ThemeSet.java +++ b/src/main/java/com/extantape/blueminimap/theme/ThemeSet.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.theme; +package com.extantape.blueminimap.theme; import java.io.IOException; import java.io.InputStream; @@ -21,7 +21,7 @@ /** * The themes the pack is built with: the built-in ones from the jar's {@code themes/} folder, plus or - * replaced by any in the server's {@code plugins/BlueMapHUD/themes/}. The pack holds every theme, so a + * replaced by any in the server's {@code plugins/BlueMinimap/themes/}. The pack holds every theme, so a * player can switch with {@code /hud theme} without a new download. */ public final class ThemeSet { diff --git a/src/main/java/com/extantape/bluemaphud/tiles/FileTileSource.java b/src/main/java/com/extantape/blueminimap/tiles/FileTileSource.java similarity index 97% rename from src/main/java/com/extantape/bluemaphud/tiles/FileTileSource.java rename to src/main/java/com/extantape/blueminimap/tiles/FileTileSource.java index 74db3a7..c820551 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/FileTileSource.java +++ b/src/main/java/com/extantape/blueminimap/tiles/FileTileSource.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/extantape/bluemaphud/tiles/GridCache.java b/src/main/java/com/extantape/blueminimap/tiles/GridCache.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/tiles/GridCache.java rename to src/main/java/com/extantape/blueminimap/tiles/GridCache.java index af3af41..0816fa5 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/GridCache.java +++ b/src/main/java/com/extantape/blueminimap/tiles/GridCache.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import java.io.IOException; import java.util.Arrays; @@ -51,7 +51,7 @@ public int cell(int localX, int localZ) { private final Logger logger; private final Set loading = ConcurrentHashMap.newKeySet(); private final ExecutorService loaders = Executors.newFixedThreadPool(2, runnable -> { - Thread thread = new Thread(runnable, "BlueMapHUD-tiles"); + Thread thread = new Thread(runnable, "BlueMinimap-tiles"); thread.setDaemon(true); thread.setPriority(Thread.NORM_PRIORITY - 1); return thread; diff --git a/src/main/java/com/extantape/bluemaphud/tiles/HttpTileSource.java b/src/main/java/com/extantape/blueminimap/tiles/HttpTileSource.java similarity index 98% rename from src/main/java/com/extantape/bluemaphud/tiles/HttpTileSource.java rename to src/main/java/com/extantape/blueminimap/tiles/HttpTileSource.java index 9dc52f8..32c0325 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/HttpTileSource.java +++ b/src/main/java/com/extantape/blueminimap/tiles/HttpTileSource.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import java.io.IOException; import java.net.URI; diff --git a/src/main/java/com/extantape/bluemaphud/tiles/LowresSettings.java b/src/main/java/com/extantape/blueminimap/tiles/LowresSettings.java similarity index 97% rename from src/main/java/com/extantape/bluemaphud/tiles/LowresSettings.java rename to src/main/java/com/extantape/blueminimap/tiles/LowresSettings.java index 43487eb..5f8c91b 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/LowresSettings.java +++ b/src/main/java/com/extantape/blueminimap/tiles/LowresSettings.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import com.google.gson.JsonArray; import com.google.gson.JsonObject; diff --git a/src/main/java/com/extantape/bluemaphud/tiles/TileSource.java b/src/main/java/com/extantape/blueminimap/tiles/TileSource.java similarity index 96% rename from src/main/java/com/extantape/bluemaphud/tiles/TileSource.java rename to src/main/java/com/extantape/blueminimap/tiles/TileSource.java index 16c4582..aab0556 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/TileSource.java +++ b/src/main/java/com/extantape/blueminimap/tiles/TileSource.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import java.io.IOException; diff --git a/src/main/java/com/extantape/bluemaphud/tiles/TileStore.java b/src/main/java/com/extantape/blueminimap/tiles/TileStore.java similarity index 99% rename from src/main/java/com/extantape/bluemaphud/tiles/TileStore.java rename to src/main/java/com/extantape/blueminimap/tiles/TileStore.java index 8670956..3132d75 100644 --- a/src/main/java/com/extantape/bluemaphud/tiles/TileStore.java +++ b/src/main/java/com/extantape/blueminimap/tiles/TileStore.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.tiles; +package com.extantape.blueminimap.tiles; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 32c3087..e2002e2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,4 +1,4 @@ -# BlueMapHUD configuration. Apply changes with /bmhud reload. +# BlueMinimap configuration. Apply changes with /blueminimap reload. tiles: # file: read BlueMap's folder directly (fastest, file storage only). @@ -36,7 +36,7 @@ hud: # Players can switch with /hud mode, and their choice is saved. default-mode: map # Colours, plate and clock style for new players: classic, galaxy, desert or high-contrast, or a theme - # of your own in plugins/BlueMapHUD/themes/. Players can choose their own with /hud theme. + # of your own in plugins/BlueMinimap/themes/. Players can choose their own with /hud theme. theme: classic clock: # A small clock and the in-game time after the coordinates, in 24-hour time. It's hidden where time @@ -57,8 +57,8 @@ hud: quick-hide-gesture: false commands: - # The first name no other plugin owns is used. /bmhud map, /bmhud hud, - # /bluemaphud:map and /bluemaphud:hud always work. + # The first name no other plugin owns is used. /blueminimap map, /blueminimap hud, + # /blueminimap:map and /blueminimap:hud always work. map-names: [map, worldmap] hud-names: [hud, minimap] @@ -96,21 +96,21 @@ markers: show-death: true pack: - # false means your own merged pack provides the files. See /bmhud pack export. + # false means your own merged pack provides the files. See /blueminimap pack export. send: true # true kicks players who decline the pack, with kick-message. A download that fails never kicks: # the player plays without the map, admins are warned, and it tries again on a world change or rejoin. required: true - kick-message: "This server needs the BlueMapHUD resource pack." - prompt: "BlueMapHUD draws the map with this small resource pack." + kick-message: "This server needs the BlueMinimap resource pack." + prompt: "BlueMinimap draws the map with this small resource pack." port: 8164 # Host players download from. Empty uses the address they connected to. host: "" # Host the pack elsewhere, such as a CDN or GitHub release. Overrides the port. # {sha1} is replaced with the built pack's hash, so the address changes when the pack does. - # Upload plugins/BlueMapHUD/pack/bluemaphud-pack.zip unchanged; re-zipping it changes the hash. + # Upload plugins/BlueMinimap/pack/blueminimap-pack.zip unchanged; re-zipping it changes the hash. public-url: "" - # On startup and /bmhud reload, download public-url once and warn if it isn't the current pack. + # On startup and /blueminimap reload, download public-url once and warn if it isn't the current pack. # Turn off if the server itself can't reach that address. verify-public-url: true # Make one boss bar colour transparent. Turn off only with the action bar. @@ -126,7 +126,7 @@ share: # Who can be offered a share: world-group is players in the sender's world, counting its _nether and # _the_end as the same world. all-online is everyone, for servers whose rules plugin narrows each share # through CoordinateShareEvent; with no such plugin listening it acts like world-group. Players who - # turned shares off (/hud hide shares) or lack bluemaphud.share.receive are never offered. + # turned shares off (/hud hide shares) or lack blueminimap.share.receive are never offered. candidates: world-group # Seconds a player waits between shares, so it can't be used to spam. cooldown-seconds: 30 diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml index a75897a..717493e 100644 --- a/src/main/resources/paper-plugin.yml +++ b/src/main/resources/paper-plugin.yml @@ -1,9 +1,9 @@ -name: BlueMapHUD +name: BlueMinimap version: '${version}' -main: com.extantape.bluemaphud.BlueMapHudPlugin +main: com.extantape.blueminimap.BlueMinimapPlugin api-version: '26.1' description: A BlueMap minimap and coordinates HUD, with a fullscreen map, drawn with a resource pack font. -website: https://github.com/ExtantApe/BlueMapHUD +website: https://github.com/ExtantApe/BlueMinimap # Commands are registered with Brigadier in code, so there is no commands section. dependencies: server: @@ -18,21 +18,39 @@ dependencies: required: false join-classpath: true permissions: - bluemaphud.use: + blueminimap.use: description: See and control the minimap. default: true - bluemaphud.fullscreen: + blueminimap.fullscreen: description: Open the fullscreen map. default: true - bluemaphud.seeplayers: + blueminimap.seeplayers: description: See other players on the map. default: true - bluemaphud.share.send: + blueminimap.share.send: description: Share your coordinates with /hud share. default: true - bluemaphud.share.receive: + blueminimap.share.receive: description: Receive coordinates other players share. default: true + blueminimap.admin: + description: Use /blueminimap reload, debug and pack commands. + default: op + bluemaphud.use: + description: The old name for blueminimap.use, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. + default: true + bluemaphud.fullscreen: + description: The old name for blueminimap.fullscreen, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. + default: true + bluemaphud.seeplayers: + description: The old name for blueminimap.seeplayers, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. + default: true + bluemaphud.share.send: + description: The old name for blueminimap.share.send, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. + default: true + bluemaphud.share.receive: + description: The old name for blueminimap.share.receive, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. + default: true bluemaphud.admin: - description: Use /bmhud reload, debug and pack commands. + description: The old name for blueminimap.admin, from before the rename. Still respected in 0.2.x (a "no" on either name wins for nodes that are on by default) and removed in the next release. default: op diff --git a/src/test/java/com/extantape/blueminimap/MigrationTest.java b/src/test/java/com/extantape/blueminimap/MigrationTest.java new file mode 100644 index 0000000..4d8c63e --- /dev/null +++ b/src/test/java/com/extantape/blueminimap/MigrationTest.java @@ -0,0 +1,59 @@ +package com.extantape.blueminimap; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +class MigrationTest { + + @Test + void copiesTheOldFolderWhenTheNewOneIsMissing(@TempDir Path plugins) throws Exception { + Path old = plugins.resolve("BlueMapHUD"); + Files.createDirectories(old.resolve("sub")); + Files.writeString(old.resolve("config.yml"), "profiles: {}\n"); + Files.writeString(old.resolve("changes.log"), "{\"a\":1}\n"); + Files.writeString(old.resolve("sub/x.json"), "[]"); + + String msg = Migration.copyIfMissing(old, plugins.resolve("BlueMinimap")); + + assertNotNull(msg); + assertEquals("profiles: {}\n", Files.readString(plugins.resolve("BlueMinimap/config.yml"))); + assertEquals("{\"a\":1}\n", Files.readString(plugins.resolve("BlueMinimap/changes.log"))); + assertEquals("[]", Files.readString(plugins.resolve("BlueMinimap/sub/x.json"))); + assertTrue(Files.exists(old.resolve("config.yml")), "the old folder is left as a backup"); + assertFalse(Files.exists(plugins.resolve("BlueMinimap.migrating"))); + } + + @Test + void neverOverwritesAnExistingNewFolder(@TempDir Path plugins) throws Exception { + Files.createDirectories(plugins.resolve("BlueMapHUD")); + Files.writeString(plugins.resolve("BlueMapHUD/config.yml"), "old"); + Files.createDirectories(plugins.resolve("BlueMinimap")); + Files.writeString(plugins.resolve("BlueMinimap/config.yml"), "new"); + + assertNull(Migration.copyIfMissing(plugins.resolve("BlueMapHUD"), plugins.resolve("BlueMinimap"))); + assertEquals("new", Files.readString(plugins.resolve("BlueMinimap/config.yml"))); + } + + @Test + void doesNothingOnAFreshServer(@TempDir Path plugins) { + assertNull(Migration.copyIfMissing(plugins.resolve("BlueMapHUD"), plugins.resolve("BlueMinimap"))); + assertFalse(Files.exists(plugins.resolve("BlueMinimap"))); + } + + @Test + void clearsAHalfFinishedCopyFromACrash(@TempDir Path plugins) throws Exception { + Files.createDirectories(plugins.resolve("BlueMapHUD")); + Files.writeString(plugins.resolve("BlueMapHUD/config.yml"), "real"); + Files.createDirectories(plugins.resolve("BlueMinimap.migrating")); + Files.writeString(plugins.resolve("BlueMinimap.migrating/junk"), "half"); + + assertNotNull(Migration.copyIfMissing(plugins.resolve("BlueMapHUD"), plugins.resolve("BlueMinimap"))); + assertEquals("real", Files.readString(plugins.resolve("BlueMinimap/config.yml"))); + assertFalse(Files.exists(plugins.resolve("BlueMinimap/junk"))); + } +} diff --git a/src/test/java/com/extantape/blueminimap/PermsTest.java b/src/test/java/com/extantape/blueminimap/PermsTest.java new file mode 100644 index 0000000..54fc513 --- /dev/null +++ b/src/test/java/com/extantape/blueminimap/PermsTest.java @@ -0,0 +1,87 @@ +package com.extantape.blueminimap; + +import org.bukkit.permissions.Permissible; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class PermsTest { + + /** A Permissible whose answers come from the map; anything missing is false. */ + private static Permissible with(Map values) { + return (Permissible) Proxy.newProxyInstance(Permissible.class.getClassLoader(), new Class[]{Permissible.class}, + (proxy, method, args) -> switch (method.getName()) { + case "hasPermission" -> values.getOrDefault((String) args[0], false); + case "toString" -> "kid"; + case "hashCode" -> System.identityHashCode(proxy); + case "equals" -> proxy == args[0]; + default -> throw new UnsupportedOperationException(method.getName()); + }); + } + + @Test + void anOldDenyOnADefaultOnNodeStillDenies() { + List log = new ArrayList<>(); + Perms.logTo(log::add); + // A children's group had bluemaphud.share.send set to false; the new node is still at its default, true. + Permissible kid = with(Map.of("blueminimap.share.send", true, "bluemaphud.share.send", false)); + assertFalse(Perms.has(kid, "blueminimap.share.send")); + assertEquals(1, log.size()); + assertTrue(log.get(0).contains("set blueminimap.share.send"), log.get(0)); + } + + @Test + void aNewDenyOnADefaultOnNodeDenies() { + Permissible p = with(Map.of("blueminimap.share.receive", false, "bluemaphud.share.receive", true)); + assertFalse(Perms.has(p, "blueminimap.share.receive")); + } + + @Test + void untouchedDefaultsAgree() { + Permissible p = with(Map.of("blueminimap.use", true, "bluemaphud.use", true)); + assertTrue(Perms.has(p, "blueminimap.use")); + assertFalse(Perms.has(with(Map.of()), "blueminimap.admin")); + } + + @Test + void anOldAdminGrantStillGrants() { + Permissible mod = with(Map.of("bluemaphud.admin", true)); + assertTrue(Perms.has(mod, "blueminimap.admin")); + } + + @Test + void everyDeclaredNodeHasAnOldTwinWithTheSameDefault() throws Exception { + String yml = new String(PermsTest.class.getResourceAsStream("/paper-plugin.yml").readAllBytes()); + for (String node : List.of("use", "fullscreen", "seeplayers", "share.send", "share.receive", "admin")) { + int now = yml.indexOf(" blueminimap." + node + ":\n"), old = yml.indexOf(" bluemaphud." + node + ":\n"); + assertTrue(now >= 0 && old >= 0, node); + String d1 = yml.substring(yml.indexOf("default:", now)).split("\n")[0]; + String d2 = yml.substring(yml.indexOf("default:", old)).split("\n")[0]; + assertEquals(d1, d2, node); + } + } + + @Test + void aNewDenyWithTheOldNodeAtItsDefaultLogsNothing() { + List log = new ArrayList<>(); + Perms.logTo(log::add); + // The admin set the new name to false; the old one is still at its default, true. Nothing to tell them. + Permissible p = with(Map.of("blueminimap.seeplayers", false, "bluemaphud.seeplayers", true)); + assertFalse(Perms.has(p, "blueminimap.seeplayers")); + assertTrue(log.isEmpty(), String.valueOf(log)); + } + + @Test + void aNewAdminGrantWithTheOldNodeAtItsDefaultLogsNothing() { + List log = new ArrayList<>(); + Perms.logTo(log::add); + Permissible p = with(Map.of("blueminimap.admin", true, "bluemaphud.admin", false)); + assertTrue(Perms.has(p, "blueminimap.admin")); + assertTrue(log.isEmpty(), String.valueOf(log)); + } +} diff --git a/src/test/java/com/extantape/bluemaphud/PlayerSettingsTest.java b/src/test/java/com/extantape/blueminimap/PlayerSettingsTest.java similarity index 97% rename from src/test/java/com/extantape/bluemaphud/PlayerSettingsTest.java rename to src/test/java/com/extantape/blueminimap/PlayerSettingsTest.java index d48984f..f23599b 100644 --- a/src/test/java/com/extantape/bluemaphud/PlayerSettingsTest.java +++ b/src/test/java/com/extantape/blueminimap/PlayerSettingsTest.java @@ -1,7 +1,7 @@ -package com.extantape.bluemaphud; +package com.extantape.blueminimap; -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.hud.PlayerSettings; +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.hud.PlayerSettings; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.persistence.PersistentDataContainer; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/extantape/bluemaphud/RenderTest.java b/src/test/java/com/extantape/blueminimap/RenderTest.java similarity index 95% rename from src/test/java/com/extantape/bluemaphud/RenderTest.java rename to src/test/java/com/extantape/blueminimap/RenderTest.java index 954ef15..2eb8445 100644 --- a/src/test/java/com/extantape/bluemaphud/RenderTest.java +++ b/src/test/java/com/extantape/blueminimap/RenderTest.java @@ -1,20 +1,20 @@ -package com.extantape.bluemaphud; - -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.hud.FrameBuilder; -import com.extantape.bluemaphud.hud.Snapshot; -import com.extantape.bluemaphud.markers.MarkerService; -import com.extantape.bluemaphud.pack.PackBuilder; -import com.extantape.bluemaphud.render.FontLayout; -import com.extantape.bluemaphud.render.HudFonts; -import com.extantape.bluemaphud.render.HudIcon; -import com.extantape.bluemaphud.render.IconSet; -import com.extantape.bluemaphud.render.MapComposer; -import com.extantape.bluemaphud.render.MapFrame; -import com.extantape.bluemaphud.render.TinyFont; -import com.extantape.bluemaphud.tiles.GridCache; -import com.extantape.bluemaphud.tiles.TileSource; -import com.extantape.bluemaphud.tiles.TileStore; +package com.extantape.blueminimap; + +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.hud.FrameBuilder; +import com.extantape.blueminimap.hud.Snapshot; +import com.extantape.blueminimap.markers.MarkerService; +import com.extantape.blueminimap.pack.PackBuilder; +import com.extantape.blueminimap.render.FontLayout; +import com.extantape.blueminimap.render.HudFonts; +import com.extantape.blueminimap.render.HudIcon; +import com.extantape.blueminimap.render.IconSet; +import com.extantape.blueminimap.render.MapComposer; +import com.extantape.blueminimap.render.MapFrame; +import com.extantape.blueminimap.render.TinyFont; +import com.extantape.blueminimap.tiles.GridCache; +import com.extantape.blueminimap.tiles.TileSource; +import com.extantape.blueminimap.tiles.TileStore; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import net.kyori.adventure.text.Component; @@ -94,7 +94,7 @@ void packUsesItsOwnNamespaceAndNoShaders() throws IOException { for (String path : client.files.keySet()) { assertFalse(path.contains("shaders"), path); assertTrue(path.equals("pack.mcmeta") - || path.startsWith("assets/bluemaphud/") + || path.startsWith("assets/blueminimap/") || path.startsWith("assets/minecraft/textures/gui/sprites/boss_bar/pink_"), path); } JsonObject pack = JsonParser.parseString(new String(client.files.get("pack.mcmeta"), StandardCharsets.UTF_8)) @@ -189,7 +189,7 @@ void framesUseOneFontAndNoShadow() { frame.drawGrid(new int[16]); Component component = frame.finish(); assertEquals("hud", component.style().font().value()); - assertEquals("bluemaphud", component.style().font().namespace()); + assertEquals("blueminimap", component.style().font().namespace()); assertEquals(ShadowColor.none(), component.style().shadowColor()); } @@ -295,8 +295,8 @@ void readoutFontAdvancesMatchTheClientFormula() throws IOException { HudConfig config = config(); HudFonts fonts = new HudFonts(config); SimulatedClient.Font font = client(config, fonts).fonts.get("hud"); - for (int glyph = 0; glyph < com.extantape.bluemaphud.render.ReadoutFont.glyphCount(); glyph++) { - assertEquals(font.glyphs().get(fonts.minimap.readoutChar(0, glyph)).advance(), com.extantape.bluemaphud.render.ReadoutFont.advance(glyph), "glyph " + glyph); + for (int glyph = 0; glyph < com.extantape.blueminimap.render.ReadoutFont.glyphCount(); glyph++) { + assertEquals(font.glyphs().get(fonts.minimap.readoutChar(0, glyph)).advance(), com.extantape.blueminimap.render.ReadoutFont.advance(glyph), "glyph " + glyph); } } @@ -384,7 +384,7 @@ void coordsOnlyDrawsJustTheReadoutAndNeedsNoMap() throws IOException { maxY = Math.max(maxY, y); } } - assertEquals(com.extantape.bluemaphud.render.FontLayout.PLATE_HEIGHT, maxY - minY + 1, "Just one plate high"); + assertEquals(com.extantape.blueminimap.render.FontLayout.PLATE_HEIGHT, maxY - minY + 1, "Just one plate high"); assertTrue(Math.abs((minX + maxX) / 2 - SCREEN_W / 2) <= 2, "Centred at the top: " + minX + " to " + maxX); assertTrue(maxX - minX < 100, "Only a short line: " + (maxX - minX)); ImageIO.write(image.getSubimage(SCREEN_W / 2 - 80, 0, 160, 40), "png", previewFile("coords-only.png")); diff --git a/src/test/java/com/extantape/bluemaphud/ShareTest.java b/src/test/java/com/extantape/blueminimap/ShareTest.java similarity index 89% rename from src/test/java/com/extantape/bluemaphud/ShareTest.java rename to src/test/java/com/extantape/blueminimap/ShareTest.java index 7bce58f..7069b39 100644 --- a/src/test/java/com/extantape/bluemaphud/ShareTest.java +++ b/src/test/java/com/extantape/blueminimap/ShareTest.java @@ -1,6 +1,6 @@ -package com.extantape.bluemaphud; +package com.extantape.blueminimap; -import com.extantape.bluemaphud.share.ShareService; +import com.extantape.blueminimap.share.ShareService; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/extantape/bluemaphud/SimulatedClient.java b/src/test/java/com/extantape/blueminimap/SimulatedClient.java similarity index 96% rename from src/test/java/com/extantape/bluemaphud/SimulatedClient.java rename to src/test/java/com/extantape/blueminimap/SimulatedClient.java index d69eae9..4ab8cae 100644 --- a/src/test/java/com/extantape/bluemaphud/SimulatedClient.java +++ b/src/test/java/com/extantape/blueminimap/SimulatedClient.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud; +package com.extantape.blueminimap; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -35,8 +35,8 @@ record Font(Map glyphs, Map spaces) {} for (ZipEntry entry; (entry = in.getNextEntry()) != null; ) files.put(entry.getName(), in.readAllBytes()); } for (String path : files.keySet()) { - if (path.startsWith("assets/bluemaphud/font/") && path.endsWith(".json")) { - String name = path.substring("assets/bluemaphud/font/".length(), path.length() - 5); + if (path.startsWith("assets/blueminimap/font/") && path.endsWith(".json")) { + String name = path.substring("assets/blueminimap/font/".length(), path.length() - 5); fonts.put(name, loadFont(new String(files.get(path), StandardCharsets.UTF_8))); } } @@ -58,7 +58,7 @@ private Font loadFont(String json) throws IOException { int height = provider.get("height").getAsInt(); int ascent = provider.get("ascent").getAsInt(); if (ascent > height) throw new AssertionError("Client rejects ascent " + ascent + " > height " + height); - String file = "assets/bluemaphud/textures/" + provider.get("file").getAsString().substring("bluemaphud:".length()); + String file = "assets/blueminimap/textures/" + provider.get("file").getAsString().substring("blueminimap:".length()); byte[] png = files.get(file); if (png == null) throw new AssertionError("Missing texture " + file); BufferedImage sheet = sheets.computeIfAbsent(file, f -> { diff --git a/src/test/java/com/extantape/bluemaphud/ThemeTest.java b/src/test/java/com/extantape/blueminimap/ThemeTest.java similarity index 95% rename from src/test/java/com/extantape/bluemaphud/ThemeTest.java rename to src/test/java/com/extantape/blueminimap/ThemeTest.java index fc4ec08..b00d488 100644 --- a/src/test/java/com/extantape/bluemaphud/ThemeTest.java +++ b/src/test/java/com/extantape/blueminimap/ThemeTest.java @@ -1,15 +1,15 @@ -package com.extantape.bluemaphud; - -import com.extantape.bluemaphud.config.HudConfig; -import com.extantape.bluemaphud.hud.FrameBuilder; -import com.extantape.bluemaphud.hud.Snapshot; -import com.extantape.bluemaphud.markers.MarkerService; -import com.extantape.bluemaphud.pack.PackBuilder; -import com.extantape.bluemaphud.render.HudFonts; -import com.extantape.bluemaphud.render.IconSet; -import com.extantape.bluemaphud.theme.Theme; -import com.extantape.bluemaphud.theme.ThemeContrast; -import com.extantape.bluemaphud.theme.ThemeSet; +package com.extantape.blueminimap; + +import com.extantape.blueminimap.config.HudConfig; +import com.extantape.blueminimap.hud.FrameBuilder; +import com.extantape.blueminimap.hud.Snapshot; +import com.extantape.blueminimap.markers.MarkerService; +import com.extantape.blueminimap.pack.PackBuilder; +import com.extantape.blueminimap.render.HudFonts; +import com.extantape.blueminimap.render.IconSet; +import com.extantape.blueminimap.theme.Theme; +import com.extantape.blueminimap.theme.ThemeContrast; +import com.extantape.blueminimap.theme.ThemeSet; import net.kyori.adventure.text.Component; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/extantape/bluemaphud/TilesTest.java b/src/test/java/com/extantape/blueminimap/TilesTest.java similarity index 95% rename from src/test/java/com/extantape/bluemaphud/TilesTest.java rename to src/test/java/com/extantape/blueminimap/TilesTest.java index 6dc1e05..da319e8 100644 --- a/src/test/java/com/extantape/bluemaphud/TilesTest.java +++ b/src/test/java/com/extantape/blueminimap/TilesTest.java @@ -1,9 +1,9 @@ -package com.extantape.bluemaphud; +package com.extantape.blueminimap; -import com.extantape.bluemaphud.tiles.GridCache; -import com.extantape.bluemaphud.tiles.LowresSettings; -import com.extantape.bluemaphud.tiles.TileSource; -import com.extantape.bluemaphud.tiles.TileStore; +import com.extantape.blueminimap.tiles.GridCache; +import com.extantape.blueminimap.tiles.LowresSettings; +import com.extantape.blueminimap.tiles.TileSource; +import com.extantape.blueminimap.tiles.TileStore; import org.junit.jupiter.api.Test; import javax.imageio.ImageIO; diff --git a/src/test/java/com/extantape/bluemaphud/pack/PublicUrlCheckTest.java b/src/test/java/com/extantape/blueminimap/pack/PublicUrlCheckTest.java similarity index 99% rename from src/test/java/com/extantape/bluemaphud/pack/PublicUrlCheckTest.java rename to src/test/java/com/extantape/blueminimap/pack/PublicUrlCheckTest.java index 0510049..f942070 100644 --- a/src/test/java/com/extantape/bluemaphud/pack/PublicUrlCheckTest.java +++ b/src/test/java/com/extantape/blueminimap/pack/PublicUrlCheckTest.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.pack; +package com.extantape.blueminimap.pack; import com.sun.net.httpserver.HttpServer; import org.junit.jupiter.api.AfterEach; diff --git a/src/test/java/com/extantape/blueminimap/share/HouseRulesGuardTest.java b/src/test/java/com/extantape/blueminimap/share/HouseRulesGuardTest.java new file mode 100644 index 0000000..a6a84bc --- /dev/null +++ b/src/test/java/com/extantape/blueminimap/share/HouseRulesGuardTest.java @@ -0,0 +1,22 @@ +package com.extantape.blueminimap.share; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class HouseRulesGuardTest { + @Test + void sharingIsOffWithHouseRulesOlderThan02() { + assertNotNull(ShareService.olderHouseRules("0.1.1")); + assertNotNull(ShareService.olderHouseRules("0.1.0")); + assertNotNull(ShareService.olderHouseRules("something odd"), "an unreadable version fails closed"); + } + + @Test + void sharingIsOnWithHouseRules02OrNewerOrWithout() { + assertNull(ShareService.olderHouseRules("0.2.0")); + assertNull(ShareService.olderHouseRules("0.2.1-SNAPSHOT")); + assertNull(ShareService.olderHouseRules("1.0.0")); + assertNull(ShareService.olderHouseRules(null)); + } +} diff --git a/test/live/README.md b/test/live/README.md index a6e46db..9c6f4fa 100644 --- a/test/live/README.md +++ b/test/live/README.md @@ -1,6 +1,6 @@ # Live tests -These run BlueMapHUD on a real Paper server with real BlueMap tiles, and drive it with a protocol bot. Every frame the bot receives is decoded and compared cell by cell with BlueMap's tile files on disk. The unit tests in `src/test` cover the rendering maths; these cover everything that needs a server. +These run BlueMinimap on a real Paper server with real BlueMap tiles, and drive it with a protocol bot. Every frame the bot receives is decoded and compared cell by cell with BlueMap's tile files on disk. The unit tests in `src/test` cover the rendering maths; these cover everything that needs a server. `run.sh` builds one throwaway server on 127.0.0.1, generates a small world with a fixed seed, has BlueMap render it, runs the tests you name, and deletes the server afterwards, including when a test fails. Only one server runs at a time. @@ -8,6 +8,7 @@ These run BlueMapHUD on a real Paper server with real BlueMap tiles, and drive i - **Real:** Paper, BlueMap, the plugin jar, the generated pack, BlueMap's tiles and SQL storage. - **A bot, not the game:** the client is [mineflayer](https://github.com/PrismarineJS/mineflayer), which speaks the 1.21.11 protocol, so it joins 26.x through ViaVersion and ViaBackwards. It doesn't draw anything. Frames are decoded with a model of the client's font rules. +- **Real HouseRules** in the `houserules` test only. - **Simulated:** the helper plugin acts like other plugins: vanish, boss bars, permissions, POI markers, a pack sent with the old one-pack method the way Magic does it, and a chat rules plugin that filters `CoordinateShareEvent`. The pack host for `public-url` is a local stand-in. - **Not covered:** pressing the buttons in the `/hud share` dialog. The bot checks the dialog it's sent, but its protocol library has no packet for clicking one. The buttons run Paper's own callbacks into the same code `/hud share ` and `/hud share everyone` test. - **Optional:** the font check runs the real Minecraft client's font code on the pack, if you give it a client classpath (see below). It's skipped otherwise, and the summary says so. @@ -47,9 +48,10 @@ BMH_ACCEPT_EULA=1 test/live/run.sh 26.1.2 suite:coordsonly,onebar | `suite` | Everything players see: map accuracy, icons, the readout, the clock, themes, sharing, hiding rules, one boss bar, updates, pack handling, reloads, icons, action bar, HTTP source, bad data. Pick groups with `suite:`, such as `suite:clock,themes,share`. | about 20 min | | `suite:clockrate` | Not part of `suite`: what the clock costs a player standing still, at steps of 1, 5 and 10 in-game minutes. | 4 min | | `load` | 20 moving bots for a minute: update size, build time and tick. Results depend on how busy the machine is. | 2 min | -| `packurl` | `public-url` with `{sha1}`, the hosted-pack check, and `/bmhud pack export`. | 3 min | +| `packurl` | `public-url` with `{sha1}`, the hosted-pack check, and `/blueminimap pack export`. | 3 min | | `storage` | BlueMap starting 8 times next to the plugin, and BlueMap on SQLite read through the HTTP source. | 6 min | | `backoff` | Another plugin owning `/map` and `/hud`, and getting them back once it's removed. | 2 min | +| `houserules` | The real HouseRules with BlueMinimap: shares stay inside chat circles with HouseRules 0.2, sharing turns itself off with HouseRules 0.1.1, and works without HouseRules. Needs `BMH_HOUSERULES_JAR` and `BMH_HOUSERULES_OLD_JAR`; skipped otherwise. | 3 min | Logs go to `test/live/work/server--.log`. Each test prints a summary, with any failures and skipped checks. diff --git a/test/live/backoff.js b/test/live/backoff.js index b399dc3..79cb1c3 100644 --- a/test/live/backoff.js +++ b/test/live/backoff.js @@ -1,4 +1,4 @@ -// Command back-off: with an old-style plugin owning /map and /hud, BlueMapHUD must take its next names, and +// Command back-off: with an old-style plugin owning /map and /hud, BlueMinimap must take its next names, and // get the short names back once that plugin is gone. // Usage: node backoff.js

, with and relative to the work folder. const { Server, Bot, check, section, summary, sleep, until } = require('./lib.js'); @@ -21,22 +21,22 @@ async function say(bot, command, pattern) { await s.start(); await sleep(4000); s.cmd('op Tester'); const names = s.log.match(/Commands: fullscreen map is (\S+), minimap is (\S+)\./); - check('BlueMapHUD backs off to /worldmap and /minimap', names && names[1] === '/worldmap' && names[2] === '/minimap', names?.[0]); + check('BlueMinimap backs off to /worldmap and /minimap', names && names[1] === '/worldmap' && names[2] === '/minimap', names?.[0]); const bot = new Bot('Tester', port); await Promise.race([bot.spawned, sleep(20000)]); await until(() => bot.packBytes, 8000); await sleep(1000); check('/map still reaches the other plugin', await say(bot, '/map', /CONFLICT PLUGIN OWNS \/map/)); check('/hud still reaches the other plugin', await say(bot, '/hud', /CONFLICT PLUGIN OWNS \/hud/)); - check('/minimap reaches BlueMapHUD', await say(bot, '/minimap off', /Minimap off/)); - check('/bmhud hud always works', await say(bot, '/bmhud hud on', /Minimap on/)); - check('/bluemaphud:hud always works', await say(bot, '/bluemaphud:hud off', /Minimap off/)); - await say(bot, '/bluemaphud:hud on', /Minimap on/); + check('/minimap reaches BlueMinimap', await say(bot, '/minimap off', /Minimap off/)); + check('/blueminimap hud always works', await say(bot, '/blueminimap hud on', /Minimap on/)); + check('/blueminimap:hud always works', await say(bot, '/blueminimap:hud off', /Minimap off/)); + await say(bot, '/blueminimap:hud on', /Minimap on/); await bot.cleanQuit(); await s.stop(); section('The other plugin is removed'); fs.rmSync(installed, { force: true }); await s.start(); await sleep(4000); const back = s.log.match(/Commands: fullscreen map is (\S+), minimap is (\S+)\./); - check('BlueMapHUD takes /map and /hud again after a restart', back && back[1] === '/map' && back[2] === '/hud', back?.[0]); + check('BlueMinimap takes /map and /hud again after a restart', back && back[1] === '/map' && back[2] === '/hud', back?.[0]); await s.stop(); } catch (e) { check('Back-off test finished', false, e.stack?.slice(0, 300)); diff --git a/test/live/conflict/src/main/java/com/extantape/bluemaphud/livetest/Conflict.java b/test/live/conflict/src/main/java/com/extantape/blueminimap/livetest/Conflict.java similarity index 91% rename from test/live/conflict/src/main/java/com/extantape/bluemaphud/livetest/Conflict.java rename to test/live/conflict/src/main/java/com/extantape/blueminimap/livetest/Conflict.java index 95a1ed1..5b25cc4 100644 --- a/test/live/conflict/src/main/java/com/extantape/bluemaphud/livetest/Conflict.java +++ b/test/live/conflict/src/main/java/com/extantape/blueminimap/livetest/Conflict.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.livetest; +package com.extantape.blueminimap.livetest; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; diff --git a/test/live/conflict/src/main/resources/plugin.yml b/test/live/conflict/src/main/resources/plugin.yml index 087fef6..90836ca 100644 --- a/test/live/conflict/src/main/resources/plugin.yml +++ b/test/live/conflict/src/main/resources/plugin.yml @@ -1,8 +1,8 @@ # Test only. Deliberately an old-style Bukkit plugin that owns /map and /hud in plugin.yml, so the live -# tests can check that BlueMapHUD backs off to other names. Never install it on a real server. +# tests can check that BlueMinimap backs off to other names. Never install it on a real server. name: LiveTestConflict version: '1' -main: com.extantape.bluemaphud.livetest.Conflict +main: com.extantape.blueminimap.livetest.Conflict api-version: '26.1' commands: map: {} diff --git a/test/live/fontcheck/FontCheck.java b/test/live/fontcheck/FontCheck.java index 397c666..7e47282 100644 --- a/test/live/fontcheck/FontCheck.java +++ b/test/live/fontcheck/FontCheck.java @@ -45,7 +45,7 @@ public static void main(String[] args) throws Exception { StringBuilder out = new StringBuilder("{"); boolean firstFont = true; for (String path : new TreeSet<>(files.keySet())) { - if (!path.startsWith("assets/bluemaphud/font/")) continue; + if (!path.startsWith("assets/blueminimap/font/")) continue; String name = path.substring(path.lastIndexOf('/') + 1, path.length() - 5); JsonArray providers = JsonParser.parseString(new String(files.get(path), StandardCharsets.UTF_8)).getAsJsonObject().getAsJsonArray("providers"); int glyphs = 0; diff --git a/test/live/helper/src/main/java/com/extantape/bluemaphud/livetest/Helper.java b/test/live/helper/src/main/java/com/extantape/blueminimap/livetest/Helper.java similarity index 93% rename from test/live/helper/src/main/java/com/extantape/bluemaphud/livetest/Helper.java rename to test/live/helper/src/main/java/com/extantape/blueminimap/livetest/Helper.java index cec01be..8d6e2a3 100644 --- a/test/live/helper/src/main/java/com/extantape/bluemaphud/livetest/Helper.java +++ b/test/live/helper/src/main/java/com/extantape/blueminimap/livetest/Helper.java @@ -1,4 +1,4 @@ -package com.extantape.bluemaphud.livetest; +package com.extantape.blueminimap.livetest; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.api.BlueMapAPI; @@ -51,7 +51,7 @@ public void execute(CommandSourceStack source, String[] args) { @Override public String permission() { - return "bluemaphud.livetest"; + return "blueminimap.livetest"; } })); } @@ -78,7 +78,10 @@ private void run(String[] a) { } case "perm" -> { Player p = player(a[1]); - permissions.computeIfAbsent(p.getUniqueId(), id -> p.addAttachment(this)).setPermission(a[2], Boolean.parseBoolean(a[3])); + // An attachment from an earlier login belongs to a Player object that's gone, so setting it would do nothing. + PermissionAttachment held = permissions.get(p.getUniqueId()); + if (held == null || held.getPermissible() != p) { held = p.addAttachment(this); permissions.put(p.getUniqueId(), held); } + held.setPermission(a[2], Boolean.parseBoolean(a[3])); p.recalculatePermissions(); p.updateCommands(); say("perm " + a[2] + " " + a[3]); @@ -175,9 +178,9 @@ private void shareFilter(String[] a) { org.bukkit.event.HandlerList.unregisterAll(shareListener); shareMode = a.length > 2 ? a[1] + " " + a[2] : a[1]; if (!a[1].equals("off")) { - getServer().getPluginManager().registerEvent(com.extantape.bluemaphud.api.CoordinateShareEvent.class, shareListener, + getServer().getPluginManager().registerEvent(com.extantape.blueminimap.api.CoordinateShareEvent.class, shareListener, org.bukkit.event.EventPriority.NORMAL, (listener, event) -> { - var share = (com.extantape.bluemaphud.api.CoordinateShareEvent) event; + var share = (com.extantape.blueminimap.api.CoordinateShareEvent) event; say("share from " + share.getSender().getName() + " at " + share.getX() + " " + share.getY() + " " + share.getZ() + " to " + share.getRecipients().stream().map(Player::getName).sorted().toList()); if (shareMode.equals("cancel")) share.setCancelled(true); diff --git a/test/live/helper/src/main/resources/paper-plugin.yml b/test/live/helper/src/main/resources/paper-plugin.yml index 33b8754..382c2f8 100644 --- a/test/live/helper/src/main/resources/paper-plugin.yml +++ b/test/live/helper/src/main/resources/paper-plugin.yml @@ -1,6 +1,6 @@ name: LiveTestHelper version: '1' -main: com.extantape.bluemaphud.livetest.Helper +main: com.extantape.blueminimap.livetest.Helper api-version: '26.1' description: Test only. Lets the live tests act like other plugins. Never install it on a real server. dependencies: @@ -10,7 +10,7 @@ dependencies: required: true join-classpath: true # For the share tests: listens to CoordinateShareEvent the way a server's chat rules plugin would. - BlueMapHUD: + BlueMinimap: load: BEFORE required: true join-classpath: true diff --git a/test/live/houserules.js b/test/live/houserules.js new file mode 100644 index 0000000..ef226a1 --- /dev/null +++ b/test/live/houserules.js @@ -0,0 +1,88 @@ +// BlueMinimap with the real HouseRules: shares follow chat circles, and with a HouseRules too old to see BlueMinimap's +// share event, sharing is off rather than unfiltered. +// Usage: node houserules.js , and relative to the work folder. +const { Server, Bot, check, section, summary, sleep, until } = require('./lib.js'); +const fs = require('fs'); +const [dir, jar, portS, newJar, oldJar] = process.argv.slice(2); +const port = +portS; +const s = new Server(dir, jar, port); +const installed = s.path('plugins', 'HouseRules.jar'); + +// Made-up circles. The overworld is circle-only, so only circles decide who hears whom there. +const CONFIG = 'circles: [red, blue]\ncircle-only-worlds: [world]\n'; + +async function say(bot, command, pattern) { + const from = bot.chat.length; + bot.say(command); + return until(() => bot.chat.slice(from).find((line) => pattern.test(line)), 5000); +} +const heard = (bot, from) => bot.chat.slice(from).some((m) => /shared where they are/.test(m)); + +async function joinAll(names) { + const bots = {}; + for (const n of names) { + bots[n] = new Bot(n, port); + await Promise.race([bots[n].spawned, sleep(20000)]); + await until(() => bots[n].packBytes, 8000); + await sleep(300); + } + return bots; +} + +(async () => { + let bots = {}; + try { + section('BlueMinimap with HouseRules 0.2: shares follow the circles'); + fs.copyFileSync(newJar, installed); + fs.mkdirSync(s.path('plugins', 'HouseRules'), { recursive: true }); + fs.writeFileSync(s.path('plugins', 'HouseRules', 'config.yml'), CONFIG); + await s.start(); await sleep(4000); + check('HouseRules found BlueMinimap\'s share event', /BlueMinimap found: coordinate shares follow the same rules as chat/.test(s.log)); + bots = await joinAll(['Tester', 'Alex', 'Blair', 'Casey']); + for (const [who, circle] of [['Tester', 'red'], ['Alex', 'red'], ['Blair', 'blue']]) s.cmd(`bmt perm ${who} houserules.circle.${circle} true`); + await sleep(800); + let marks = Object.fromEntries(Object.entries(bots).map(([n, b]) => [n, b.chat.length])); + const sent = await say(bots.Tester, '/hud share everyone', /Sent your coordinates|weren't sent|can receive/); + await sleep(600); + check('A share reaches the sender\'s circle', heard(bots.Alex, marks.Alex), sent); + check('and nobody outside it', !heard(bots.Blair, marks.Blair) && !heard(bots.Casey, marks.Casey), sent); + marks = Object.fromEntries(Object.entries(bots).map(([n, b]) => [n, b.chat.length])); + const lonely = await say(bots.Casey, '/hud share everyone', /Sent your coordinates|weren't sent|can receive|can't/); + await sleep(600); + check('Someone with no circle in a circle-only world sends nothing', !Object.entries(bots).some(([n, b]) => n !== 'Casey' && heard(b, marks[n])), lonely); + for (const b of Object.values(bots)) await b.cleanQuit(); + await s.stop(); + + section('BlueMinimap with HouseRules 0.1.1: sharing is off, not unfiltered'); + fs.copyFileSync(oldJar, installed); + await s.start(); await sleep(4000); + bots = await joinAll(['Tester', 'Alex', 'Blair']); + s.cmd('bmt perm Tester houserules.circle.red true'); s.cmd('bmt perm Alex houserules.circle.red true'); await sleep(800); + marks = Object.fromEntries(Object.entries(bots).map(([n, b]) => [n, b.chat.length])); + const mark = s.mark(); + const off = await say(bots.Tester, '/hud share everyone', /Sent your coordinates|off on this server|can receive/); + await sleep(600); + check('The sender is told sharing is off', /Sharing coordinates is off on this server for now/.test(off || ''), off); + check('and nobody receives anything', !heard(bots.Alex, marks.Alex) && !heard(bots.Blair, marks.Blair)); + check('The log tells the admin to update HouseRules', /HouseRules 0\.1\.1 can't see BlueMinimap's shares.*Update HouseRules to 0\.2\.0/.test(s.since(mark))); + for (const b of Object.values(bots)) await b.cleanQuit(); + await s.stop(); + + section('BlueMinimap without HouseRules: sharing works'); + fs.rmSync(installed, { force: true }); + await s.start(); await sleep(4000); + bots = await joinAll(['Tester', 'Alex']); + const m = bots.Alex.chat.length; + const plain = await say(bots.Tester, '/hud share everyone', /Sent your coordinates|off on this server|can receive/); + await sleep(600); + check('control: without HouseRules a share goes through', /Sent your coordinates/.test(plain || '') && heard(bots.Alex, m), plain); + for (const b of Object.values(bots)) await b.cleanQuit(); + await s.stop(); + } catch (e) { + check('houserules finished', false, e.stack?.slice(0, 400)); + for (const b of Object.values(bots)) try { await b.cleanQuit(); } catch {} + try { await s.stop(); } catch {} + } + fs.rmSync(installed, { force: true }); + summary(); +})(); diff --git a/test/live/lib.js b/test/live/lib.js index 1fe3536..476d0fd 100644 --- a/test/live/lib.js +++ b/test/live/lib.js @@ -1,4 +1,4 @@ -// Live test kit for BlueMapHUD: server control, a protocol bot, a frame decoder, and an independent +// Live test kit for BlueMinimap: server control, a protocol bot, a frame decoder, and an independent // reading of BlueMap's tiles to compare frames against. See README.md in this folder. const path = require('path'), fs = require('fs'), zlib = require('zlib'), cp = require('child_process'), crypto = require('crypto'); /** Holds the server jars, plugins and throwaway servers. Never committed. */ @@ -81,10 +81,10 @@ class Server { await sleep(500); } errors(from = 0) { - return this.log.slice(from).split('\n').filter((l) => /\bERROR\b|Exception|\bat com\.extantape|BlueMapHUD.*WARN|WARN.*BlueMapHUD/.test(l) && !/Advanced terminal/.test(l)); + return this.log.slice(from).split('\n').filter((l) => /\bERROR\b|Exception|\bat com\.extantape|BlueMinimap.*WARN|WARN.*BlueMinimap/.test(l) && !/Advanced terminal/.test(l)); } path(...p) { return path.join(this.dir, ...p); } - setConfig(edits) { editYaml(this.path('plugins/BlueMapHUD/config.yml'), edits); } + setConfig(edits) { editYaml(this.path('plugins/BlueMinimap/config.yml'), edits); } } /** Sets scalar values by dotted path, keeping everything else in the file. */ @@ -146,7 +146,7 @@ class Pack { this.files = unzipAll(tmp); fs.unlinkSync(tmp); this.fonts = {}; for (const [name, data] of Object.entries(this.files)) { - const m = name.match(/^assets\/bluemaphud\/font\/(\w+)\.json$/); + const m = name.match(/^assets\/blueminimap\/font\/(\w+)\.json$/); if (m) this.fonts[m[1]] = this.loadFont(JSON.parse(data.toString('utf8'))); } } @@ -155,7 +155,7 @@ class Pack { for (const p of font.providers) { if (p.type === 'space') { for (const [k, v] of Object.entries(p.advances)) map.set(k.codePointAt(0), { kind: 'space', advance: v }); continue; } if (p.ascent > p.height) throw new Error(`ascent ${p.ascent} > height ${p.height}`); - const file = 'assets/bluemaphud/textures/' + p.file.split(':')[1]; + const file = 'assets/blueminimap/textures/' + p.file.split(':')[1]; const sheet = sheets[file] || (sheets[file] = readPng(this.files[file])); const rows = p.chars.map((s) => [...s]), cols = rows[0].length; const cw = sheet.w / cols, ch = sheet.h / rows.length, scale = p.height / ch; @@ -312,7 +312,7 @@ class Bot { if (this.downloadStatus === 200) { const res = await fetch(p.url), buf = Buffer.from(await res.arrayBuffer()); this.packSha = crypto.createHash('sha1').update(buf).digest('hex'); - if (p.url.includes('bluemaphud')) { this.packUrl = p.url; this.packBytes = buf; } + if (p.url.includes('blueminimap')) { this.packUrl = p.url; this.packBytes = buf; } } const send = (result) => this.bot._client.write('resource_pack_receive', { uuid: p.uuid, result }); if (this.mode === 'ignore') return; diff --git a/test/live/package-lock.json b/test/live/package-lock.json index 5972574..be99f60 100644 --- a/test/live/package-lock.json +++ b/test/live/package-lock.json @@ -1,10 +1,10 @@ { - "name": "bluemaphud-live-tests", + "name": "blueminimap-live-tests", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "bluemaphud-live-tests", + "name": "blueminimap-live-tests", "license": "MIT", "dependencies": { "mineflayer": "4.39.0" diff --git a/test/live/package.json b/test/live/package.json index 7ea8e15..498204a 100644 --- a/test/live/package.json +++ b/test/live/package.json @@ -1,7 +1,7 @@ { - "name": "bluemaphud-live-tests", + "name": "blueminimap-live-tests", "private": true, - "description": "Live tests for BlueMapHUD: throwaway Paper servers driven by a protocol bot.", + "description": "Live tests for BlueMinimap: throwaway Paper servers driven by a protocol bot.", "license": "MIT", "type": "commonjs", "engines": { diff --git a/test/live/packurl.js b/test/live/packurl.js index 519e7e2..38b8973 100644 --- a/test/live/packurl.js +++ b/test/live/packurl.js @@ -1,4 +1,4 @@ -// Live test for pack.public-url with {sha1}, the hosted-pack check, and /bmhud pack export. +// Live test for pack.public-url with {sha1}, the hosted-pack check, and /blueminimap pack export. // Usage: node packurl.js