Skip to content

maicra999/MapCube

Repository files navigation

MapCube

One modern, responsive web frontend that unifies BlueMap (3D) and squaremap (2D) maps behind a single, config-driven map selector.

It reuses each plugin's own renderer instead of re-implementing rendering, so it keeps working across plugin updates with minimal adjustment:

  • squaremap maps are drawn natively with Leaflet, reusing squaremap's exact tile scheme and CRS.Simple projection (src/backends/squaremap).
  • BlueMap maps are shown by embedding BlueMap's shipped webapp in an iframe (src/backends/bluemap), deep-linked to the selected map. BlueMap's 3D WebGL engine is not published as a library, so embedding is the update-proof path: upgrade BlueMap → drop in its new web/ build → no frontend changes.

Shared across both backends: the map selector, responsive chrome (desktop rail / mobile bottom-sheet), a unified online players list, a points-of-interest panel, deep-linkable URLs, and theming/branding.

Tech stack

React 19 · Vite · TypeScript · Tailwind CSS v4 · Leaflet · zod. Package manager: bun.

Quick start

bun install
bun run dev        # http://localhost:5173  — opens the offline "Demo (mock)" 2D map

The default public/config.json ships a demo map backed by mock data in public/mock/squaremap, so the app renders a working 2D map (tiles, two players, two POIs) with no server. Point the other entries at your real plugin outputs (below) and remove the demo when ready.

bun run build      # typecheck + production build to dist/
bun run preview    # serve the production build
bun test           # unit tests (config validation + squaremap projection)

Configuration

Everything is driven by public/config.json, validated at startup against public/config.schema.json. It is read at runtime — edit it in a deployed build to change maps/branding without rebuilding. An invalid config shows an on-screen error explaining what's wrong.

{
  "branding": {
    "title": "Server Maps",
    "subtitle": "Live world maps",
    "logo": null,                 // URL/path, or null for an auto initial badge
    "themeColor": "#3b82f6",      // accent color (any CSS color)
    "defaultMapId": "overworld"   // map opened on first load
  },
  "bluemap": { "hideUi": "css-addon" },   // see "Hiding BlueMap's UI" below
  "maps": [
    { "id": "overworld", "label": "Overworld", "icon": "🌍",
      "backend": "squaremap", "url": "/maps/squaremap", "world": "minecraft_overworld" },

    { "id": "world-3d", "label": "Overworld (3D)", "icon": "🧊",
      "backend": "bluemap", "url": "/maps/bluemap", "map": "world" }
  ]
}

Per-map fields:

field backend meaning
url both Web root of the plugin output. squaremap: the dir containing tiles/. BlueMap: the dir containing settings.json + maps/.
world squaremap World name as in tiles/settings.jsonworlds[].name.
map bluemap BlueMap map id (used as the iframe URL hash, e.g. #world).

Customizing UI text (i18n)

All user-facing UI copy lives in public/strings.json (validated against public/strings.schema.json) and is loaded at runtime — edit it in a deployed build to translate or reword the UI without rebuilding. Every key is optional; omitted keys fall back to the built-in English default, and a missing/invalid file just uses all defaults (the app never fails over text). A few values take {placeholders} (e.g. loadingMap, mapLoadError support {map}). Branding (title, subtitle, …) stays in config.json.

// public/strings.json — override only what you want
{ "playersHeading": "Spieler", "noPlayers": "Keine Spieler auf dieser Karte.", "loadingMap": "Lade {map}…" }

Deployment & CORS (important)

  • BlueMap (iframe) works cross-origin with no special setup.

  • squaremap (native fetch) loads settings.json, tiles and players.json with fetch/<img> from this app's origin. The clean setup is a reverse proxy that serves the plugin outputs same-origin under this app, e.g.

    location /maps/squaremap/ { proxy_pass http://127.0.0.1:8080/; }  # squaremap integrated server
    location /maps/bluemap/   { proxy_pass http://127.0.0.1:8100/; }  # BlueMap integrated server

    Then all urls are same-origin and everything (including the BlueMap iframe) is CORS-clean. Alternatively, enable CORS on squaremap's integrated HTTP server and use absolute urls.

For local dev, uncomment the server.proxy block in vite.config.ts to point /maps/* at your running servers.

Hiding BlueMap's own UI (optional, recommended)

Because this app already provides the selector/title/player list, you can hide BlueMap's duplicate chrome inside the iframe. Two routes, set via bluemap.hideUi in config:

  • css-addon (default, update-safe): Copy public/bluemap-embed.css and public/bluemap-embed.js into BlueMap's web root and reference them from BlueMap's settings.json — this is BlueMap's intended extension hook (BlueMapApp.js injects styles[]/scripts[]):

    // bluemap/web/settings.json
    "scripts": ["bluemap-embed.js"],
    "styles":  ["bluemap-embed.css"]

    The script adds an embedded class only when BlueMap runs inside our iframe, so opening BlueMap directly is unaffected. The CSS hides the chrome. No BlueMap fork, survives upgrades.

  • inject: The parent injects the CSS into the iframe at load. Requires BlueMap served same-origin (reverse proxy). More direct but couples to BlueMap's CSS class names.

  • none: Leave BlueMap's UI as-is.

Updating the base plugins

Project layout

src/
  config/        loadConfig + zod schema (runtime-validated config.json)
  backends/
    types.ts                 MapBackendAdapter interface + normalized Player/Marker
    createAdapter.ts         factory by backend
    squaremap/               native Leaflet renderer (client, tile layer, projection, adapter)
    bluemap/                 iframe embed (adapter) + live-data client
  components/    Sidebar, MapSelector, PlayerList, MarkerList, BrandingHeader, MobileDrawer, Viewport
  state/         useHashRoute (deep-link router: #/<mapId>?x=&z=&zoom=)
  App.tsx        shell orchestration
public/
  config.json, config.schema.json
  bluemap-embed.css, bluemap-embed.js     (copy into BlueMap's web root)
  mock/squaremap/                          offline demo dataset

How the shared abstraction works

Each backend implements MapBackendAdapter (mount/unmount/focus/getView/ getPlayers/getMarkers). Viewport owns the active adapter's lifecycle, polls live data into the shared panels, and writes the current view into the URL for shareable deep-links. squaremap renders its own player markers on the Leaflet map; BlueMap renders players/markers inside its iframe — in both cases the same React sidebar lists them.

About

A shared frontend for BlueMap and squaremap.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors