diff --git a/package.json b/package.json index 7738bd1c2..9c89e3edb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "google-translate-api-x": "^10.7.1", "groq-sdk": "^0.15.0", "minecraft-data": "^3.97.0", + "minecraft-assets": "^1.16.0", "mineflayer": "^4.33.0", "mineflayer-armor-manager": "^2.0.1", "mineflayer-auto-eat": "^3.3.6", diff --git a/settings.js b/settings.js index e59457db6..2ea4dee4d 100644 --- a/settings.js +++ b/settings.js @@ -57,7 +57,6 @@ const settings = { "block_place_delay": 0, // delay between placing blocks (ms) if using newAction. helps avoid bot being kicked by anti-cheat mechanisms on servers. "log_all_prompts": false, // log ALL prompts to file - -} +}; export default settings; diff --git a/src/mindcraft/mindserver.js b/src/mindcraft/mindserver.js index 1397553ec..a46485156 100644 --- a/src/mindcraft/mindserver.js +++ b/src/mindcraft/mindserver.js @@ -54,6 +54,65 @@ export function createMindServer(host_public = false, port = 8080) { const __dirname = path.dirname(fileURLToPath(import.meta.url)); app.use(express.static(path.join(__dirname, 'public'))); + // Texture proxy: resolve item/block textures using minecraft-assets with version fallback + app.get('/assets/item/:agent/:name.png', async (req, res) => { + try { + const agentName = req.params.agent; + const rawName = req.params.name; + const itemName = String(rawName).toLowerCase(); + const conn = agent_connections[agentName]; + const preferred = conn?.settings?.minecraft_version; + const candidates = []; + if (preferred && preferred !== 'auto') candidates.push(preferred); + candidates.push('1.21.8'); + + // Lazy import to avoid ESM/CJS conflicts + const mod = await import('minecraft-assets'); + const mcAssetsFactory = mod.default || mod; + + for (const ver of candidates) { + try { + const assets = mcAssetsFactory(ver); + // Prefer items path first, then blocks + const item = assets.items[itemName]; + const block = assets.blocks[itemName]; + const tex = assets.textureContent?.[itemName]?.texture + || (item ? assets.textureContent?.[itemName]?.texture : null) + || (block ? assets.textureContent?.[itemName]?.texture : null); + if (tex) { + // textureContent already provides a data URL in many versions + if (tex.startsWith('data:image')) { + const base64 = tex.split(',')[1]; + const img = globalThis.Buffer.from(base64, 'base64'); + res.setHeader('Content-Type', 'image/png'); + return res.end(img); + } + } + // If textureContent missing, try static path resolution inside package + // Helps with some strange blocks like Leaf Litter + const guessPaths = []; + const base = assets.directory; + guessPaths.push(path.join(base, 'items', `${itemName}.png`)); + guessPaths.push(path.join(base, 'blocks', `${itemName}.png`)); + for (const p of guessPaths) { + try { + const fsMod = await import('fs'); + const buf = fsMod.readFileSync(p); + res.setHeader('Content-Type', 'image/png'); + return res.end(buf); + } catch { /* ignore */ } + } + } catch { /* ignore */ } + } + // Not found, fallback svg + res.setHeader('Content-Type', 'image/svg+xml'); + res.status(404).send(''); + } catch (e) { + res.setHeader('Content-Type', 'image/svg+xml'); + res.status(500).send(''); + } + }); + // Socket.io connection handling io.on('connection', (socket) => { let curAgentName = null; @@ -191,7 +250,7 @@ export function createMindServer(host_public = false, port = 8080) { // wait 2 seconds setTimeout(() => { console.log('Exiting MindServer'); - process.exit(0); + globalThis.process.exit(0); }, 2000); }); @@ -199,10 +258,10 @@ export function createMindServer(host_public = false, port = 8080) { socket.on('send-message', (agentName, data) => { if (!agent_connections[agentName]) { console.warn(`Agent ${agentName} not in game, cannot send message via MindServer.`); - return + return; } try { - agent_connections[agentName].socket.emit('send-message', data) + agent_connections[agentName].socket.emit('send-message', data); } catch (error) { console.error('Error: ', error); } diff --git a/src/mindcraft/public/index.html b/src/mindcraft/public/index.html index b6bcdaf9e..c8ce5b800 100644 --- a/src/mindcraft/public/index.html +++ b/src/mindcraft/public/index.html @@ -165,6 +165,69 @@ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 6px; } + /* Inventory item visuals */ + .inventory-grid.empty { + display: grid; + grid-template-columns: 1fr; + color: #999; + } + .inv-item { + position: relative; + display: grid; + grid-template-rows: auto auto; + justify-items: center; + align-items: center; + gap: 4px; + background: #2f2f2f; + border: 1px solid #444; + border-radius: 6px; + padding: 8px; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.03); + } + .inv-img { + width: 32px; + height: 32px; + image-rendering: pixelated; + image-rendering: crisp-edges; + } + .inv-count { + position: absolute; + right: 6px; + bottom: 6px; + background: rgba(0,0,0,0.6); + color: #fff; + font-size: 0.8em; + padding: 2px 6px; + border-radius: 10px; + } + .inv-name { + font-size: 0.85em; + color: #ccc; + text-align: center; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + /* Armor icons */ + .armor-grid { + display: flex; + align-items: center; + gap: 8px; + } + .armor-slot { + position: relative; + width: 36px; + height: 36px; + border-radius: 6px; + background: #2f2f2f; + border: 1px solid #444; + display: flex; + align-items: center; + justify-content: center; + } + .armor-slot.empty { opacity: 0.4; } + .armor-placeholder { color: #888; font-size: 0.9em; } .agent-details-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); @@ -386,6 +449,64 @@