From 79eca14f4cd7499ba2e837e59aa76bf73da040dc Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Sat, 21 Mar 2026 19:57:13 -0400 Subject: [PATCH] Add dropper location selector: discover available droppers on network, pass through server, and add dropdown in order panel for location-based dispensing --- web/client/src/components/InventoryGrid.css | 45 ++++++ web/client/src/components/InventoryGrid.jsx | 23 ++- web/client/src/components/ItemIcon.jsx | 164 ++++++++++++++------ web/client/src/store/inventoryStore.js | 1 + web/server/server.js | 17 ++ 5 files changed, 204 insertions(+), 46 deletions(-) diff --git a/web/client/src/components/InventoryGrid.css b/web/client/src/components/InventoryGrid.css index fadf7ba..c002605 100644 --- a/web/client/src/components/InventoryGrid.css +++ b/web/client/src/components/InventoryGrid.css @@ -412,6 +412,51 @@ flex-wrap: wrap; } +/* Dropper location selector */ +.dropper-select-wrapper { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.dropper-label { + font-size: 0.65rem; + color: var(--mc-text-gold); + text-shadow: 1px 1px 0 #000; +} + +.dropper-select { + background: #2a2a2a; + border: 2px solid #444; + color: var(--mc-text-white); + font-family: 'Silkscreen', 'Courier New', monospace; + font-size: 0.7rem; + padding: 0.375rem 0.5rem; + outline: none; + border-radius: 0; + cursor: pointer; + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23aaa'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.5rem center; + padding-right: 1.5rem; +} + +.dropper-select:hover { + border-color: var(--mc-text-green); +} + +.dropper-select:focus { + border-color: var(--mc-text-green); + box-shadow: inset 0 0 4px rgba(85, 255, 85, 0.15); +} + +.dropper-select option { + background: #1a1a1a; + color: var(--mc-text-white); +} + .order-btn { width: 100%; padding: 0.625rem !important; diff --git a/web/client/src/components/InventoryGrid.jsx b/web/client/src/components/InventoryGrid.jsx index 24e56c7..4151ee0 100644 --- a/web/client/src/components/InventoryGrid.jsx +++ b/web/client/src/components/InventoryGrid.jsx @@ -15,6 +15,9 @@ function InventoryGrid() { const [orderAmount, setOrderAmount] = useState(1); const [sortBy, setSortBy] = useState('count'); const [activeCategory, setActiveCategory] = useState('all'); + const [selectedDropper, setSelectedDropper] = useState(''); + + const droppers = inventory.droppers || []; const items = getFilteredItems(); @@ -33,8 +36,8 @@ function InventoryGrid() { const handleOrder = useCallback(async () => { if (!selectedItem || orderAmount <= 0) return; - await orderItem(selectedItem.name, orderAmount); - }, [selectedItem, orderAmount, orderItem]); + await orderItem(selectedItem.name, orderAmount, selectedDropper || undefined); + }, [selectedItem, orderAmount, orderItem, selectedDropper]); const handleItemClick = (item) => { setSelectedItem(item); @@ -162,6 +165,22 @@ function InventoryGrid() {

📤 Order Items

+ {droppers.length > 1 && ( +
+ + +
+ )}
= 0 ? fullItemName.substring(0, colonIdx) : 'minecraft'; const shortName = colonIdx >= 0 ? fullItemName.substring(colonIdx + 1) : fullItemName; - const alias = TEXTURE_ALIASES[shortName] || shortName; const urls = []; - // For non-minecraft mods, try mod-specific URLs first - if (namespace !== 'minecraft') { - const knownMods = ['computercraft', 'create']; - if (knownMods.includes(namespace)) { - urls.push(`${TEXTURE_PROXY_BASE}/${namespace}/item/${shortName}.png`); - urls.push(`${TEXTURE_PROXY_BASE}/${namespace}/block/${shortName}.png`); - urls.push(`${TEXTURE_PROXY_BASE}/${namespace}/block/${shortName}_front.png`); - urls.push(`${TEXTURE_PROXY_BASE}/${namespace}/block/${shortName}_side.png`); + // CC:Tweaked → use curated texture map + if (namespace === 'computercraft') { + const mapped = CC_TEXTURE_MAP[shortName]; + if (mapped) { + urls.push(`${TEXTURE_PROXY_BASE}/computercraft/${mapped}.png`); + } else { + urls.push(`${TEXTURE_PROXY_BASE}/computercraft/item/${shortName}.png`); + urls.push(`${TEXTURE_PROXY_BASE}/computercraft/block/${shortName}.png`); } + return urls; } - // Vanilla texture URLs (through proxy) - if (BLOCK_TEXTURES.has(shortName)) { - const suffix = BLOCK_TEXTURE_SUFFIXES[shortName] || ''; + // Create mod → item/ then block/ + if (namespace === 'create') { + urls.push(`${TEXTURE_PROXY_BASE}/create/item/${shortName}.png`); + urls.push(`${TEXTURE_PROXY_BASE}/create/block/${shortName}.png`); + return urls; + } + + // Unknown mod namespace → no textures available, instant emoji fallback + if (namespace !== 'minecraft') { + return urls; + } + + // === Vanilla (minecraft) === + const alias = TEXTURE_ALIASES[shortName] || shortName; + + // Derivative blocks (stairs, slabs, fences, walls, buttons) → parent texture + const parent = resolveDerivativeTexture(alias); + if (parent) { + const suffix = BLOCK_TEXTURE_SUFFIXES[parent] || ''; + urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${parent}${suffix}.png`); + return urls; + } + + // Known block → try block/ first (with suffix if applicable) + if (BLOCK_TEXTURES.has(alias)) { + const suffix = BLOCK_TEXTURE_SUFFIXES[alias] || ''; urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}${suffix}.png`); if (suffix) urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}.png`); } + // Try item/ texture urls.push(`${TEXTURE_PROXY_BASE}/minecraft/item/${alias}.png`); - if (!BLOCK_TEXTURES.has(shortName)) { + // If not a known block, also try block/ as last resort + if (!BLOCK_TEXTURES.has(alias)) { urls.push(`${TEXTURE_PROXY_BASE}/minecraft/block/${alias}.png`); } diff --git a/web/client/src/store/inventoryStore.js b/web/client/src/store/inventoryStore.js index 184ab37..9c02a93 100644 --- a/web/client/src/store/inventoryStore.js +++ b/web/client/src/store/inventoryStore.js @@ -45,6 +45,7 @@ export const useInventoryStore = create((set, get) => ({ barrelOk: false, furnaceCount: 0, furnaceStatus: {}, + droppers: [], }, activity: {}, alerts: [], diff --git a/web/server/server.js b/web/server/server.js index 5e2f921..705420c 100644 --- a/web/server/server.js +++ b/web/server/server.js @@ -169,6 +169,23 @@ app.get('/api/texture/:namespace/*', async (req, res) => { } }); +// Clear negative texture cache (.miss files) — call after updating texture mappings +app.delete('/api/texture-cache/negative', (req, res) => { + let cleared = 0; + function walk(dir) { + try { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walk(full); + else if (entry.name.endsWith('.miss')) { fs.unlinkSync(full); cleared++; } + } + } catch (_) { /* ignore */ } + } + walk(TEXTURE_CACHE_DIR); + console.log(`[Texture Cache] Cleared ${cleared} negative cache entries`); + res.json({ cleared }); +}); + // Health check app.get('/api/health', (req, res) => { res.json({