Files
mc-bot-town/nodejs/controller/storage/map-renderer.js
T
2026-04-30 12:48:29 -04:00

140 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const { PNG } = require('pngjs');
// Minecraft map color palette (base colors × 4 shades each)
// Source: https://minecraft.wiki/w/Map_item_format#Color_table
// Index 0-3 = NONE (transparent), 4-7 = GRASS, 8-11 = SAND, etc.
// Each base color has 4 multipliers: 0.71, 0.86, 1.0, 0.53
const BASE_COLORS = [
null, // 0: NONE
[127, 178, 56], // 1: GRASS
[247, 233, 163], // 2: SAND
[199, 199, 199], // 3: WOOL
[255, 0, 0], // 4: FIRE
[160, 160, 255], // 5: ICE
[167, 167, 167], // 6: METAL
[0, 124, 0], // 7: PLANT
[255, 255, 255], // 8: SNOW
[164, 168, 184], // 9: CLAY
[151, 109, 77], // 10: DIRT
[112, 112, 112], // 11: STONE
[64, 64, 255], // 12: WATER
[143, 119, 72], // 13: WOOD
[255, 252, 245], // 14: QUARTZ
[216, 127, 51], // 15: COLOR_ORANGE
[178, 76, 216], // 16: COLOR_MAGENTA
[102, 153, 216], // 17: COLOR_LIGHT_BLUE
[229, 229, 51], // 18: COLOR_YELLOW
[127, 204, 25], // 19: COLOR_LIGHT_GREEN
[242, 127, 165], // 20: COLOR_PINK
[76, 76, 76], // 21: COLOR_GRAY
[153, 153, 153], // 22: COLOR_LIGHT_GRAY
[76, 127, 153], // 23: COLOR_CYAN
[127, 63, 178], // 24: COLOR_PURPLE
[51, 76, 178], // 25: COLOR_BLUE
[102, 76, 51], // 26: COLOR_BROWN
[102, 127, 51], // 27: COLOR_GREEN
[153, 51, 51], // 28: COLOR_RED
[25, 25, 25], // 29: COLOR_BLACK
[250, 238, 77], // 30: GOLD
[92, 219, 213], // 31: DIAMOND
[74, 128, 255], // 32: LAPIS
[0, 217, 58], // 33: EMERALD
[129, 86, 49], // 34: PODZOL
[112, 2, 0], // 35: NETHER
[209, 177, 161], // 36: TERRACOTTA_WHITE
[159, 82, 36], // 37: TERRACOTTA_ORANGE
[149, 87, 108], // 38: TERRACOTTA_MAGENTA
[112, 108, 138], // 39: TERRACOTTA_LIGHT_BLUE
[186, 133, 36], // 40: TERRACOTTA_YELLOW
[103, 117, 53], // 41: TERRACOTTA_LIGHT_GREEN
[160, 77, 78], // 42: TERRACOTTA_PINK
[57, 41, 35], // 43: TERRACOTTA_GRAY
[135, 107, 98], // 44: TERRACOTTA_LIGHT_GRAY
[87, 92, 92], // 45: TERRACOTTA_CYAN
[122, 73, 88], // 46: TERRACOTTA_PURPLE
[76, 62, 92], // 47: TERRACOTTA_BLUE
[76, 50, 35], // 48: TERRACOTTA_BROWN
[76, 82, 42], // 49: TERRACOTTA_GREEN
[142, 60, 46], // 50: TERRACOTTA_RED
[37, 22, 16], // 51: TERRACOTTA_BLACK
[189, 48, 49], // 52: CRIMSON_NYLIUM
[148, 63, 97], // 53: CRIMSON_STEM
[92, 25, 29], // 54: CRIMSON_HYPHAE
[22, 126, 134], // 55: WARPED_NYLIUM
[58, 142, 140], // 56: WARPED_STEM
[86, 44, 62], // 57: WARPED_HYPHAE
[20, 180, 133], // 58: WARPED_WART_BLOCK
[100, 100, 100], // 59: DEEPSLATE
[216, 175, 147], // 60: RAW_IRON
[127, 167, 150], // 61: GLOW_LICHEN
];
const SHADE_MULTIPLIERS = [180, 220, 255, 135]; // out of 255
// Build the full 256-entry lookup: MAP_COLORS[colorIndex] → [r, g, b]
const MAP_COLORS = new Array(256).fill(null);
for (let base = 0; base < BASE_COLORS.length; base++) {
for (let shade = 0; shade < 4; shade++) {
const idx = base * 4 + shade;
if (!BASE_COLORS[base]) {
MAP_COLORS[idx] = [0, 0, 0, 0]; // transparent
} else {
const [r, g, b] = BASE_COLORS[base];
const m = SHADE_MULTIPLIERS[shade];
MAP_COLORS[idx] = [
Math.floor(r * m / 255),
Math.floor(g * m / 255),
Math.floor(b * m / 255),
255
];
}
}
}
/**
* Apply a partial map update from a protocol packet to a pixel buffer.
* @param {Uint8Array} pixels - 128×128 color index buffer (mutated in place)
* @param {Object} packet - Map protocol packet with columns, rows, x, y, data
*/
function applyMapUpdate(pixels, packet) {
const { columns, rows, x, y, data } = packet;
if (!columns || columns === 0 || !data) return;
for (let col = 0; col < columns; col++) {
for (let row = 0; row < rows; row++) {
const srcIdx = col * rows + row;
const dstX = x + col;
const dstY = y + row;
if (dstX < 128 && dstY < 128) {
pixels[dstY * 128 + dstX] = data[srcIdx];
}
}
}
}
/**
* Render a 128×128 color-index buffer to a base64 PNG string.
* @param {Uint8Array} pixels - 128×128 array of Minecraft color indices
* @returns {string} base64-encoded PNG
*/
function renderMapToPNG(pixels) {
const png = new PNG({ width: 128, height: 128 });
for (let i = 0; i < 128 * 128; i++) {
const colorIdx = pixels[i];
const color = MAP_COLORS[colorIdx] || [0, 0, 0, 0];
const offset = i * 4;
png.data[offset] = color[0]; // R
png.data[offset + 1] = color[1]; // G
png.data[offset + 2] = color[2]; // B
png.data[offset + 3] = color[3]; // A
}
const buffer = PNG.sync.write(png);
return buffer.toString('base64');
}
module.exports = { MAP_COLORS, applyMapUpdate, renderMapToPNG };