minecraft-inventory 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -10
- package/package.json +20 -8
- package/src/components/CursorItem/CursorItem.tsx +1 -10
- package/src/components/InventoryOverlay/InventoryOverlay.tsx +126 -56
- package/src/components/InventoryWindow/HotbarExtras.tsx +180 -0
- package/src/components/InventoryWindow/InventoryBackground.tsx +3 -3
- package/src/components/InventoryWindow/InventoryWindow.tsx +11 -0
- package/src/components/JEI/JEI.tsx +98 -29
- package/src/components/Notes/Notes.tsx +237 -0
- package/src/components/Notes/index.ts +2 -0
- package/src/components/RecipeGuide/RecipeInventoryView.tsx +5 -15
- package/src/components/Slot/Slot.tsx +49 -17
- package/src/components/Text/MessageFormatted.tsx +1 -1
- package/src/components/Tooltip/Tooltip.module.css +14 -14
- package/src/components/Tooltip/Tooltip.tsx +31 -24
- package/src/connector/demo.ts +4 -0
- package/src/connector/mineflayer.ts +145 -1
- package/src/connector/types.ts +39 -9
- package/src/context/TextureContext.tsx +17 -11
- package/src/generated/localTextures.ts +112 -0
- package/src/globals.d.ts +4 -0
- package/src/index.tsx +3 -2
- package/src/registry/index.ts +1 -0
- package/src/registry/inventories.ts +203 -195
- package/src/types.ts +4 -2
- package/src/utils/globalMouse.ts +14 -0
- package/src/components/Hotbar/Hotbar.tsx +0 -180
- package/src/components/Hotbar/index.ts +0 -1
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
.tooltip {
|
|
2
2
|
background: #100010;
|
|
3
3
|
border: 1px solid #100010;
|
|
4
|
-
border-radius:
|
|
4
|
+
border-radius: 3px;
|
|
5
5
|
display: flex;
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
font-family: 'Minecraftia', 'Minecraft', monospace;
|
|
8
8
|
image-rendering: pixelated;
|
|
9
9
|
line-height: 1.2;
|
|
10
|
-
/*
|
|
10
|
+
/* Gray inner border instead of classic purple */
|
|
11
11
|
box-shadow:
|
|
12
|
-
inset 0 1px 0 0 #
|
|
13
|
-
inset 1px 0 0 0 #
|
|
14
|
-
inset -1px 0 0 0 #
|
|
15
|
-
inset 0 -1px 0 0 #
|
|
12
|
+
inset 0 1px 0 0 #555555,
|
|
13
|
+
inset 1px 0 0 0 #555555,
|
|
14
|
+
inset -1px 0 0 0 #555555,
|
|
15
|
+
inset 0 -1px 0 0 #444444,
|
|
16
16
|
0 0 0 1px #100010;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.name {
|
|
20
20
|
font-weight: bold;
|
|
21
21
|
white-space: nowrap;
|
|
22
|
-
margin-bottom:
|
|
22
|
+
margin-bottom: 1px;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.section {
|
|
@@ -37,20 +37,20 @@
|
|
|
37
37
|
flex-direction: column;
|
|
38
38
|
color: #6b29a4;
|
|
39
39
|
font-style: italic;
|
|
40
|
-
border-top: 1px solid #
|
|
41
|
-
margin-top:
|
|
42
|
-
padding-top:
|
|
40
|
+
border-top: 1px solid #333333;
|
|
41
|
+
margin-top: 2px;
|
|
42
|
+
padding-top: 2px;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
.durability {
|
|
46
46
|
color: #dddddd;
|
|
47
|
-
border-top: 1px solid #
|
|
48
|
-
margin-top:
|
|
49
|
-
padding-top:
|
|
47
|
+
border-top: 1px solid #333333;
|
|
48
|
+
margin-top: 2px;
|
|
49
|
+
padding-top: 2px;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.typeId {
|
|
53
53
|
color: #444455;
|
|
54
54
|
font-size: 0.8em;
|
|
55
|
-
margin-top:
|
|
55
|
+
margin-top: 1px;
|
|
56
56
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import React, { useRef,
|
|
1
|
+
import React, { useRef, useEffect } from 'react'
|
|
2
2
|
import { createPortal } from 'react-dom'
|
|
3
3
|
import type { ItemStack } from '../../types'
|
|
4
4
|
import { useScale } from '../../context/ScaleContext'
|
|
5
5
|
import { MessageFormattedString } from '../Text/MessageFormattedString'
|
|
6
|
+
import { globalMouse } from '../../utils/globalMouse'
|
|
6
7
|
import styles from './Tooltip.module.css'
|
|
7
8
|
|
|
8
9
|
interface TooltipProps {
|
|
9
10
|
item: ItemStack
|
|
10
|
-
mouseX: number
|
|
11
|
-
mouseY: number
|
|
12
11
|
visible: boolean
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -22,16 +21,15 @@ function formatEnchantment(e: { name: string; level: number }): string {
|
|
|
22
21
|
return e.level <= 10 ? `${e.name} ${roman[e.level]}` : `${e.name} ${e.level}`
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export function Tooltip({ item,
|
|
24
|
+
export function Tooltip({ item, visible }: TooltipProps) {
|
|
26
25
|
const { scale } = useScale()
|
|
27
26
|
const ref = useRef<HTMLDivElement>(null)
|
|
28
|
-
const [pos, setPos] = useState({ x: -9999, y: -9999 })
|
|
29
|
-
const [ready, setReady] = useState(false)
|
|
30
27
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
// Position the tooltip directly via DOM, bypassing React state updates.
|
|
29
|
+
// Called on every mousemove and whenever visible/item/scale changes.
|
|
30
|
+
const applyPosition = () => {
|
|
34
31
|
const el = ref.current
|
|
32
|
+
if (!el) return
|
|
35
33
|
const tw = el.offsetWidth
|
|
36
34
|
const th = el.offsetHeight
|
|
37
35
|
const vw = window.innerWidth
|
|
@@ -39,21 +37,29 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
|
|
|
39
37
|
const gapX = 12
|
|
40
38
|
const gapY = 2
|
|
41
39
|
|
|
42
|
-
let x =
|
|
43
|
-
let y =
|
|
40
|
+
let x = globalMouse.x + gapX
|
|
41
|
+
let y = globalMouse.y - th - gapY // above cursor by default
|
|
44
42
|
|
|
45
|
-
if (x + tw > vw - 4) x =
|
|
46
|
-
if (y < 4) y =
|
|
43
|
+
if (x + tw > vw - 4) x = globalMouse.x - tw - gapX
|
|
44
|
+
if (y < 4) y = globalMouse.y + gapY // flip below if no space above
|
|
47
45
|
if (y + th > vh - 4) y = vh - th - 4
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
el.style.left = `${x}px`
|
|
48
|
+
el.style.top = `${y}px`
|
|
49
|
+
el.style.visibility = 'visible'
|
|
50
|
+
}
|
|
52
51
|
|
|
53
|
-
// Reset ready when tooltip becomes hidden
|
|
54
52
|
useEffect(() => {
|
|
55
|
-
if (!visible)
|
|
56
|
-
|
|
53
|
+
if (!visible) return
|
|
54
|
+
|
|
55
|
+
// Initial position immediately after mounting / becoming visible
|
|
56
|
+
applyPosition()
|
|
57
|
+
|
|
58
|
+
const onMove = () => applyPosition()
|
|
59
|
+
window.addEventListener('mousemove', onMove, { passive: true })
|
|
60
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
61
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
|
+
}, [visible, item, scale])
|
|
57
63
|
|
|
58
64
|
if (!visible) return null
|
|
59
65
|
|
|
@@ -64,8 +70,8 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
|
|
|
64
70
|
item.durability < item.maxDurability
|
|
65
71
|
|
|
66
72
|
const fs = Math.round(8 * scale)
|
|
67
|
-
const pad = Math.round(
|
|
68
|
-
const gap2 = Math.round(
|
|
73
|
+
const pad = Math.round(3 * scale)
|
|
74
|
+
const gap2 = Math.round(0.5 * scale)
|
|
69
75
|
|
|
70
76
|
const tooltip = (
|
|
71
77
|
<div
|
|
@@ -73,15 +79,16 @@ export function Tooltip({ item, mouseX, mouseY, visible }: TooltipProps) {
|
|
|
73
79
|
className={['mc-inv-tooltip', styles.tooltip].join(' ')}
|
|
74
80
|
style={{
|
|
75
81
|
position: 'fixed',
|
|
76
|
-
left:
|
|
77
|
-
top:
|
|
82
|
+
left: -9999,
|
|
83
|
+
top: -9999,
|
|
78
84
|
zIndex: 10000,
|
|
79
85
|
fontSize: fs,
|
|
80
86
|
padding: pad,
|
|
81
87
|
gap: gap2,
|
|
82
88
|
minWidth: Math.round(80 * scale),
|
|
83
89
|
maxWidth: Math.round(220 * scale),
|
|
84
|
-
|
|
90
|
+
// Start invisible; applyPosition sets visibility after measuring dimensions
|
|
91
|
+
visibility: 'hidden',
|
|
85
92
|
pointerEvents: 'none',
|
|
86
93
|
}}
|
|
87
94
|
>
|
package/src/connector/demo.ts
CHANGED
|
@@ -181,6 +181,10 @@ export function createDemoConnector(options: DemoConnectorOptions): InventoryCon
|
|
|
181
181
|
emit({ type: 'windowClose' })
|
|
182
182
|
},
|
|
183
183
|
|
|
184
|
+
openPlayerInventory: () => {
|
|
185
|
+
// Demo: nothing to ride, just a no-op (player inventory lives in playerState)
|
|
186
|
+
},
|
|
187
|
+
|
|
184
188
|
subscribe: (listener: ConnectorListener) => {
|
|
185
189
|
listeners.add(listener)
|
|
186
190
|
return () => listeners.delete(listener)
|
|
@@ -29,8 +29,47 @@ function modeFromAction(action: InventoryAction): [number, number] {
|
|
|
29
29
|
return [action.button === 'left' ? 0 : 1, 0]
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** Extended bot API from mineflayer plugins (villager, enchantment_table, anvil, beacon). */
|
|
33
|
+
interface MineflayerBotExtended extends MineflayerBot {
|
|
34
|
+
trade?(villager: unknown, tradeIndex: number, count?: number): Promise<void>
|
|
35
|
+
supportFeature?(name: string): boolean
|
|
36
|
+
_client?: {
|
|
37
|
+
write(packet: string, data: unknown): void
|
|
38
|
+
writeChannel?(channel: string, data: unknown): void
|
|
39
|
+
registerChannel?(channel: string, schema: unknown): void
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
function isVillagerWindow(win: unknown): win is { trade: (i: number, c?: number) => Promise<void> } {
|
|
45
|
+
return (
|
|
46
|
+
win != null &&
|
|
47
|
+
typeof (win as Record<string, unknown>).trade === 'function' &&
|
|
48
|
+
Array.isArray((win as Record<string, unknown>).trades)
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isEnchantmentTableWindow(win: unknown): win is { enchant: (choice: number) => Promise<void> } {
|
|
53
|
+
return win != null && typeof (win as Record<string, unknown>).enchant === 'function'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isAnvilWindow(
|
|
57
|
+
win: unknown
|
|
58
|
+
): win is {
|
|
59
|
+
rename: (item: unknown, name: string) => Promise<void>
|
|
60
|
+
findInventoryItem?: (id: number) => unknown
|
|
61
|
+
slots?: Array<{ type?: number; metadata?: number; count?: number; nbt?: unknown } | null>
|
|
62
|
+
} {
|
|
63
|
+
return win != null && typeof (win as Record<string, unknown>).rename === 'function'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isBeaconWindow(win: unknown): win is { setBeaconEffects?: (primary: number, secondary: number) => Promise<void> } {
|
|
67
|
+
return win != null && (typeof (win as Record<string, unknown>).setBeaconEffects === 'function' || /beacon/i.test(String((win as Record<string, unknown>).type)))
|
|
68
|
+
}
|
|
69
|
+
|
|
32
70
|
export function createMineflayerConnector(bot: MineflayerBot): InventoryConnector {
|
|
33
71
|
const listeners = new Set<ConnectorListener>()
|
|
72
|
+
const ext = bot as MineflayerBotExtended
|
|
34
73
|
|
|
35
74
|
function emit(event: ConnectorEvent) {
|
|
36
75
|
listeners.forEach((l) => l(event))
|
|
@@ -77,18 +116,123 @@ export function createMineflayerConnector(bot: MineflayerBot): InventoryConnecto
|
|
|
77
116
|
bot.on('windowClose', onWindowClose)
|
|
78
117
|
bot.on('setSlot', onSetSlot)
|
|
79
118
|
|
|
119
|
+
async function openPlayerInventory() {
|
|
120
|
+
const vehicle = bot.vehicle
|
|
121
|
+
let inventoryType = 'player'
|
|
122
|
+
|
|
123
|
+
if (vehicle) {
|
|
124
|
+
const entityName = String(vehicle.name ?? vehicle.entityType ?? '').toLowerCase()
|
|
125
|
+
// Check if riding a llama (includes trader_llama)
|
|
126
|
+
if (/llama/i.test(entityName)) {
|
|
127
|
+
inventoryType = 'llama'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Build a window state for the player/llama inventory
|
|
132
|
+
const slots: SlotState[] = []
|
|
133
|
+
|
|
134
|
+
if (inventoryType === 'llama') {
|
|
135
|
+
// Llama inventory structure (per registry):
|
|
136
|
+
// Slot 0: Carpet (saddle) - empty since we don't have entity data
|
|
137
|
+
slots.push({ index: 0, item: null })
|
|
138
|
+
// Slot 1: skipped (not used)
|
|
139
|
+
// Slots 2-16: Llama chest (5×3 grid = 15 slots) - empty since we don't have entity data
|
|
140
|
+
for (let i = 2; i <= 16; i++) {
|
|
141
|
+
slots.push({ index: i, item: null })
|
|
142
|
+
}
|
|
143
|
+
// Slots 17-43: Player inventory (bot.inventory.slots indices 9-35 map to window slots 17-43)
|
|
144
|
+
for (let i = 9; i <= 35; i++) {
|
|
145
|
+
slots.push({ index: i + 8, item: botSlotToItemStack(bot.inventory.slots[i]) })
|
|
146
|
+
}
|
|
147
|
+
// Slots 44-52: Hotbar (bot.inventory.slots indices 36-44 map to window slots 44-52)
|
|
148
|
+
for (let i = 36; i <= 44; i++) {
|
|
149
|
+
slots.push({ index: i + 8, item: botSlotToItemStack(bot.inventory.slots[i]) })
|
|
150
|
+
}
|
|
151
|
+
// Slot 53: Offhand (bot.inventory slot 45 maps to window slot 53)
|
|
152
|
+
slots.push({ index: 53, item: botSlotToItemStack(bot.inventory.slots[45]) })
|
|
153
|
+
} else {
|
|
154
|
+
// Player inventory window structure (per registry):
|
|
155
|
+
// Slots 0-8: Crafting result + grid + armor (not in bot.inventory.slots, leave empty)
|
|
156
|
+
for (let i = 0; i < 9; i++) {
|
|
157
|
+
slots.push({ index: i, item: null })
|
|
158
|
+
}
|
|
159
|
+
// Slots 9-35: Player inventory (bot.inventory.slots indices 9-35)
|
|
160
|
+
for (let i = 9; i <= 35; i++) {
|
|
161
|
+
slots.push({ index: i, item: botSlotToItemStack(bot.inventory.slots[i]) })
|
|
162
|
+
}
|
|
163
|
+
// Slots 36-44: Hotbar (bot.inventory.slots indices 36-44)
|
|
164
|
+
for (let i = 36; i <= 44; i++) {
|
|
165
|
+
slots.push({ index: i, item: botSlotToItemStack(bot.inventory.slots[i]) })
|
|
166
|
+
}
|
|
167
|
+
// Slot 45: Offhand (bot.inventory slot 45)
|
|
168
|
+
slots.push({ index: 45, item: botSlotToItemStack(bot.inventory.slots[45]) })
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const windowState: InventoryWindowState = {
|
|
172
|
+
windowId: -1, // Use -1 to indicate a synthetic/UI-only window
|
|
173
|
+
type: inventoryType,
|
|
174
|
+
title: inventoryType === 'llama' ? 'Llama' : undefined,
|
|
175
|
+
slots,
|
|
176
|
+
heldItem: botSlotToItemStack(bot.heldItem),
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
emit({ type: 'windowOpen', state: windowState })
|
|
180
|
+
}
|
|
181
|
+
|
|
80
182
|
return {
|
|
81
183
|
getWindowState: buildWindowState,
|
|
82
184
|
getPlayerState: buildPlayerState,
|
|
185
|
+
openPlayerInventory,
|
|
83
186
|
|
|
84
187
|
sendAction: async (action: InventoryAction) => {
|
|
188
|
+
// Hotbar "open inventory" button — delegates to openPlayerInventory()
|
|
189
|
+
if (action.type === 'open-inventory') {
|
|
190
|
+
await openPlayerInventory()
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const win = bot.currentWindow
|
|
195
|
+
|
|
196
|
+
if (action.type === 'trade' && win) {
|
|
197
|
+
if (ext.trade && isVillagerWindow(win)) {
|
|
198
|
+
await ext.trade(win, action.tradeIndex, 1)
|
|
199
|
+
} else if (isVillagerWindow(win)) {
|
|
200
|
+
await win.trade(action.tradeIndex, 1)
|
|
201
|
+
}
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (action.type === 'enchant' && win && isEnchantmentTableWindow(win)) {
|
|
206
|
+
await win.enchant(action.enchantIndex)
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (action.type === 'rename' && win && isAnvilWindow(win)) {
|
|
211
|
+
const w = win as { slots?: unknown[]; findInventoryItem?: (id: number) => unknown; rename: (item: unknown, name: string) => Promise<void> }
|
|
212
|
+
const inputSlot = w.slots?.[0] as { type?: number; metadata?: number; count?: number; nbt?: unknown } | null
|
|
213
|
+
const item = inputSlot?.type ? (w.findInventoryItem?.(inputSlot.type) ?? inputSlot) : null
|
|
214
|
+
if (item) await w.rename(item, action.text)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (action.type === 'beacon' && win && isBeaconWindow(win)) {
|
|
219
|
+
if (typeof win.setBeaconEffects === 'function') {
|
|
220
|
+
await win.setBeaconEffects(action.primaryEffect, action.secondaryEffect)
|
|
221
|
+
} else if (ext._client) {
|
|
222
|
+
ext._client.write('beacon_effect', {
|
|
223
|
+
primaryEffect: action.primaryEffect,
|
|
224
|
+
secondaryEffect: action.secondaryEffect,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
85
230
|
if (action.type === 'click') {
|
|
86
231
|
const [mouseButton, mode] = modeFromAction(action)
|
|
87
232
|
await bot.clickWindow(action.slotIndex, mouseButton, mode)
|
|
88
233
|
} else if (action.type === 'drop') {
|
|
89
234
|
await bot.clickWindow(action.slotIndex, action.all ? 1 : 0, 4)
|
|
90
235
|
} else if (action.type === 'close') {
|
|
91
|
-
const win = bot.currentWindow
|
|
92
236
|
if (win) bot.closeWindow(win)
|
|
93
237
|
} else if (action.type === 'hotbar-swap') {
|
|
94
238
|
await bot.clickWindow(action.slotIndex, action.hotbarSlot, 2)
|
package/src/connector/types.ts
CHANGED
|
@@ -6,6 +6,12 @@ export interface InventoryConnector {
|
|
|
6
6
|
sendAction(action: InventoryAction): void | Promise<void>
|
|
7
7
|
closeWindow(): void | Promise<void>
|
|
8
8
|
subscribe(listener: ConnectorListener): () => void
|
|
9
|
+
/**
|
|
10
|
+
* Open the player inventory, taking into account the entity the player is riding.
|
|
11
|
+
* For horse/llama/donkey/etc this activates the entity so the server sends a windowOpen.
|
|
12
|
+
* For unmounted players it emits a playerUpdate with the current inventory.
|
|
13
|
+
*/
|
|
14
|
+
openPlayerInventory(): void | Promise<void>
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
export type ConnectorEvent =
|
|
@@ -17,25 +23,49 @@ export type ConnectorEvent =
|
|
|
17
23
|
|
|
18
24
|
export type ConnectorListener = (event: ConnectorEvent) => void
|
|
19
25
|
|
|
26
|
+
export interface MineflayerBotWindow {
|
|
27
|
+
id: number
|
|
28
|
+
type: string
|
|
29
|
+
title?: string
|
|
30
|
+
slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Villager window from bot.openVillager() — has trade() */
|
|
34
|
+
export interface MineflayerVillagerWindow extends MineflayerBotWindow {
|
|
35
|
+
trade: (index: number, count?: number) => Promise<void>
|
|
36
|
+
trades: unknown[] | null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Enchantment table from bot.openEnchantmentTable() — has enchant() */
|
|
40
|
+
export interface MineflayerEnchantmentTableWindow extends MineflayerBotWindow {
|
|
41
|
+
enchant: (choice: number) => Promise<void>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Anvil from bot.openAnvil() — has rename() and combine() */
|
|
45
|
+
export interface MineflayerAnvilWindow extends MineflayerBotWindow {
|
|
46
|
+
rename: (item: { type: number; metadata?: number; count: number; nbt?: unknown }, name: string) => Promise<void>
|
|
47
|
+
combine: (itemOne: unknown, itemTwo: unknown, name?: string) => Promise<void>
|
|
48
|
+
findInventoryItem: (itemId: number) => unknown | null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Beacon — mineflayer has no dedicated plugin; uses packets. */
|
|
52
|
+
export interface MineflayerBeaconWindow extends MineflayerBotWindow {}
|
|
53
|
+
|
|
20
54
|
export interface MineflayerBot {
|
|
21
55
|
inventory: {
|
|
22
56
|
slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
|
|
23
57
|
}
|
|
24
|
-
currentWindow:
|
|
25
|
-
id: number
|
|
26
|
-
type: string
|
|
27
|
-
title?: string
|
|
28
|
-
slots: Array<{ type: number; count: number; metadata?: number; nbt?: unknown } | null>
|
|
29
|
-
} | null
|
|
58
|
+
currentWindow: MineflayerBotWindow | null
|
|
30
59
|
heldItem: { type: number; count: number; metadata?: number; nbt?: unknown } | null
|
|
31
|
-
health: number
|
|
32
|
-
food: number
|
|
33
|
-
foodSaturation: number
|
|
34
60
|
experience: { level: number; progress: number; points: number }
|
|
35
61
|
quickBarSlot: number
|
|
62
|
+
/** Entity currently being ridden, if any. Name is the mineflayer entity type string. */
|
|
63
|
+
vehicle?: { name?: string; entityType?: string; id?: number; [key: string]: unknown } | null
|
|
36
64
|
clickWindow(slot: number, mouseButton: number, mode: number): Promise<void>
|
|
37
65
|
closeWindow(window: unknown): void
|
|
38
66
|
on(event: string, listener: (...args: unknown[]) => void): void
|
|
39
67
|
off(event: string, listener: (...args: unknown[]) => void): void
|
|
40
68
|
removeListener(event: string, listener: (...args: unknown[]) => void): void
|
|
69
|
+
/** For beacon/special packets; optional. */
|
|
70
|
+
_client?: { write: (name: string, data: unknown) => void }
|
|
41
71
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import React, { createContext, useContext } from 'react'
|
|
2
2
|
|
|
3
|
-
// Default
|
|
3
|
+
// Default texture source: zardoy/mc-assets gh-pages
|
|
4
|
+
export const MC_ASSETS_BASE =
|
|
5
|
+
'https://raw.githubusercontent.com/zardoy/mc-assets/refs/heads/gh-pages'
|
|
4
6
|
export const MC_ITEMS_BASE =
|
|
5
|
-
|
|
6
|
-
export const MC_GUI_BASE =
|
|
7
|
-
'https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data'
|
|
7
|
+
`${MC_ASSETS_BASE}/1.21.11/textures`
|
|
8
|
+
export const MC_GUI_BASE = MC_ASSETS_BASE
|
|
8
9
|
|
|
9
10
|
export interface TextureConfig {
|
|
10
11
|
baseUrl: string
|
|
11
12
|
getItemTextureUrl(item: { type: number; name?: string }): string
|
|
12
13
|
getBlockTextureUrl(item: { type: number; name?: string }): string
|
|
13
|
-
/**
|
|
14
|
+
/** Supports full mc-assets paths (e.g. "1.21.11/textures/gui/container/anvil.png") */
|
|
14
15
|
getGuiTextureUrl(path: string, version?: string): string
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function buildDefault(base: string): TextureConfig {
|
|
18
19
|
const isRemote = base.startsWith('http')
|
|
19
|
-
// For remote default: use
|
|
20
|
+
// For remote default: use standardized versioned paths from mc-assets.
|
|
20
21
|
const guiBase = isRemote ? MC_GUI_BASE : base
|
|
21
22
|
|
|
22
23
|
return {
|
|
@@ -24,19 +25,24 @@ function buildDefault(base: string): TextureConfig {
|
|
|
24
25
|
|
|
25
26
|
getItemTextureUrl({ type, name }) {
|
|
26
27
|
const root = isRemote ? MC_ITEMS_BASE : base
|
|
27
|
-
if (name) return `${root}/
|
|
28
|
-
return `${root}/
|
|
28
|
+
if (name) return `${root}/item/${name}.png`
|
|
29
|
+
return `${root}/item/${type}.png`
|
|
29
30
|
},
|
|
30
31
|
|
|
31
32
|
getBlockTextureUrl({ type, name }) {
|
|
32
33
|
const root = isRemote ? MC_ITEMS_BASE : base
|
|
33
|
-
if (name) return `${root}/
|
|
34
|
-
return `${root}/
|
|
34
|
+
if (name) return `${root}/block/${name}.png`
|
|
35
|
+
return `${root}/block/${type}.png`
|
|
35
36
|
},
|
|
36
37
|
|
|
37
38
|
getGuiTextureUrl(path: string, version = '1.16.4') {
|
|
38
39
|
if (isRemote) {
|
|
39
|
-
|
|
40
|
+
// New format: "1.21.11/textures/gui/container/anvil.png"
|
|
41
|
+
if (path.endsWith('.png') && path.includes('/textures/')) {
|
|
42
|
+
return `${guiBase}/${path}`
|
|
43
|
+
}
|
|
44
|
+
// Backward compatibility with legacy "gui/container/anvil" + version fields.
|
|
45
|
+
return `${guiBase}/${version}/textures/${path}.png`
|
|
40
46
|
}
|
|
41
47
|
return `${base}/textures/${path}.png`
|
|
42
48
|
},
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Auto-generated by scripts/generate-texture-imports.mjs — do not edit manually
|
|
2
|
+
// Re-run `node scripts/generate-texture-imports.mjs` after changing inventories.
|
|
3
|
+
|
|
4
|
+
import _gui_container_inventory from 'mc-assets/dist/other-textures/latest/gui/container/inventory.png'
|
|
5
|
+
import _gui_container_shulker_box from 'mc-assets/dist/other-textures/latest/gui/container/shulker_box.png'
|
|
6
|
+
import _gui_container_generic_54 from 'mc-assets/dist/other-textures/latest/gui/container/generic_54.png'
|
|
7
|
+
import _gui_container_crafting_table from 'mc-assets/dist/other-textures/latest/gui/container/crafting_table.png'
|
|
8
|
+
import _gui_container_furnace from 'mc-assets/dist/other-textures/latest/gui/container/furnace.png'
|
|
9
|
+
import _gui_container_blast_furnace from 'mc-assets/dist/other-textures/latest/gui/container/blast_furnace.png'
|
|
10
|
+
import _gui_container_smoker from 'mc-assets/dist/other-textures/latest/gui/container/smoker.png'
|
|
11
|
+
import _gui_container_brewing_stand from 'mc-assets/dist/other-textures/latest/gui/container/brewing_stand.png'
|
|
12
|
+
import _gui_container_anvil from 'mc-assets/dist/other-textures/latest/gui/container/anvil.png'
|
|
13
|
+
import _gui_container_grindstone from 'mc-assets/dist/other-textures/latest/gui/container/grindstone.png'
|
|
14
|
+
import _gui_container_enchanting_table from 'mc-assets/dist/other-textures/latest/gui/container/enchanting_table.png'
|
|
15
|
+
import _gui_container_smithing from 'mc-assets/dist/other-textures/latest/gui/container/smithing.png'
|
|
16
|
+
import _gui_container_hopper from 'mc-assets/dist/other-textures/latest/gui/container/hopper.png'
|
|
17
|
+
import _gui_container_dispenser from 'mc-assets/dist/other-textures/latest/gui/container/dispenser.png'
|
|
18
|
+
import _gui_container_beacon from 'mc-assets/dist/other-textures/latest/gui/container/beacon.png'
|
|
19
|
+
import _gui_container_horse from 'mc-assets/dist/other-textures/latest/gui/container/horse.png'
|
|
20
|
+
import _gui_container_villager2 from 'mc-assets/dist/other-textures/latest/gui/container/villager2.png'
|
|
21
|
+
import _gui_container_cartography_table from 'mc-assets/dist/other-textures/latest/gui/container/cartography_table.png'
|
|
22
|
+
import _gui_container_loom from 'mc-assets/dist/other-textures/latest/gui/container/loom.png'
|
|
23
|
+
import _gui_container_stonecutter from 'mc-assets/dist/other-textures/latest/gui/container/stonecutter.png'
|
|
24
|
+
import _gui_container_crafter from 'mc-assets/dist/other-textures/latest/gui/container/crafter.png'
|
|
25
|
+
import _gui_container_creative_inventory_tab_items from 'mc-assets/dist/other-textures/latest/gui/container/creative_inventory/tab_items.png'
|
|
26
|
+
import _gui_widgets from 'mc-assets/dist/other-textures/1.15/gui/widgets.png'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps each inventory type name to its texture path (version prefix stripped).
|
|
30
|
+
* Useful for building custom texture resolvers or inspecting the full texture list.
|
|
31
|
+
*/
|
|
32
|
+
export const allContainerPaths: Record<string, string> = {
|
|
33
|
+
player: 'gui/container/inventory.png',
|
|
34
|
+
chest: 'gui/container/shulker_box.png',
|
|
35
|
+
generic_9x1: 'gui/container/shulker_box.png',
|
|
36
|
+
large_chest: 'gui/container/generic_54.png',
|
|
37
|
+
crafting_table: 'gui/container/crafting_table.png',
|
|
38
|
+
furnace: 'gui/container/furnace.png',
|
|
39
|
+
blast_furnace: 'gui/container/blast_furnace.png',
|
|
40
|
+
smoker: 'gui/container/smoker.png',
|
|
41
|
+
brewing_stand: 'gui/container/brewing_stand.png',
|
|
42
|
+
anvil: 'gui/container/anvil.png',
|
|
43
|
+
grindstone: 'gui/container/grindstone.png',
|
|
44
|
+
enchanting_table: 'gui/container/enchanting_table.png',
|
|
45
|
+
smithing_table: 'gui/container/smithing.png',
|
|
46
|
+
smithing_table_legacy: 'gui/container/smithing.png',
|
|
47
|
+
hopper: 'gui/container/hopper.png',
|
|
48
|
+
dispenser: 'gui/container/dispenser.png',
|
|
49
|
+
dropper: 'gui/container/dispenser.png',
|
|
50
|
+
beacon: 'gui/container/beacon.png',
|
|
51
|
+
horse: 'gui/container/horse.png',
|
|
52
|
+
donkey: 'gui/container/horse.png',
|
|
53
|
+
llama: 'gui/container/horse.png',
|
|
54
|
+
villager: 'gui/container/villager2.png',
|
|
55
|
+
shulker_box: 'gui/container/shulker_box.png',
|
|
56
|
+
barrel: 'gui/container/shulker_box.png',
|
|
57
|
+
cartography_table: 'gui/container/cartography_table.png',
|
|
58
|
+
loom: 'gui/container/loom.png',
|
|
59
|
+
stonecutter: 'gui/container/stonecutter.png',
|
|
60
|
+
crafter: 'gui/container/crafter.png',
|
|
61
|
+
creative: 'gui/container/creative_inventory/tab_items.png',
|
|
62
|
+
hotbar: 'gui/widgets.png',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Internal: full versioned texture path → bundled asset URL (or undefined = not found)
|
|
66
|
+
const _map: Record<string, string | undefined> = {
|
|
67
|
+
'1.21.11/textures/gui/container/inventory.png': _gui_container_inventory,
|
|
68
|
+
'1.21.11/textures/gui/container/shulker_box.png': _gui_container_shulker_box,
|
|
69
|
+
'1.21.11/textures/gui/container/generic_54.png': _gui_container_generic_54,
|
|
70
|
+
'1.21.11/textures/gui/container/crafting_table.png': _gui_container_crafting_table,
|
|
71
|
+
'1.21.11/textures/gui/container/furnace.png': _gui_container_furnace,
|
|
72
|
+
'1.21.11/textures/gui/container/blast_furnace.png': _gui_container_blast_furnace,
|
|
73
|
+
'1.21.11/textures/gui/container/smoker.png': _gui_container_smoker,
|
|
74
|
+
'1.21.11/textures/gui/container/brewing_stand.png': _gui_container_brewing_stand,
|
|
75
|
+
'1.21.11/textures/gui/container/anvil.png': _gui_container_anvil,
|
|
76
|
+
'1.21.11/textures/gui/container/grindstone.png': _gui_container_grindstone,
|
|
77
|
+
'1.21.11/textures/gui/container/enchanting_table.png': _gui_container_enchanting_table,
|
|
78
|
+
'1.21.11/textures/gui/container/smithing.png': _gui_container_smithing,
|
|
79
|
+
'1.16.4/textures/gui/container/smithing.png': _gui_container_smithing,
|
|
80
|
+
'1.21.11/textures/gui/container/hopper.png': _gui_container_hopper,
|
|
81
|
+
'1.21.11/textures/gui/container/dispenser.png': _gui_container_dispenser,
|
|
82
|
+
'1.21.11/textures/gui/container/beacon.png': _gui_container_beacon,
|
|
83
|
+
'1.21.11/textures/gui/container/horse.png': _gui_container_horse,
|
|
84
|
+
'1.14/textures/gui/container/villager2.png': _gui_container_villager2,
|
|
85
|
+
'1.21.11/textures/gui/container/cartography_table.png': _gui_container_cartography_table,
|
|
86
|
+
'1.21.11/textures/gui/container/loom.png': _gui_container_loom,
|
|
87
|
+
'1.21.11/textures/gui/container/stonecutter.png': _gui_container_stonecutter,
|
|
88
|
+
'1.21.11/textures/gui/container/crafter.png': _gui_container_crafter,
|
|
89
|
+
'1.21.11/textures/gui/container/creative_inventory/tab_items.png': _gui_container_creative_inventory_tab_items,
|
|
90
|
+
'1.15/textures/gui/widgets.png': _gui_widgets,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Partial TextureConfig that resolves inventory GUI textures from locally bundled
|
|
95
|
+
* mc-assets assets instead of remote GitHub URLs.
|
|
96
|
+
*
|
|
97
|
+
* Pass to `<TextureProvider config={localTexturesConfig}>` to use offline/bundled assets:
|
|
98
|
+
*
|
|
99
|
+
* ```tsx
|
|
100
|
+
* import { localTexturesConfig } from './generated/localTextures'
|
|
101
|
+
* <TextureProvider config={localTexturesConfig}>
|
|
102
|
+
* <InventoryOverlay type="chest" ... />
|
|
103
|
+
* </TextureProvider>
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export const localTexturesConfig = {
|
|
107
|
+
getGuiTextureUrl(path: string): string {
|
|
108
|
+
// _map keys are the full versioned paths used in inventories.ts
|
|
109
|
+
// e.g. '1.21.11/textures/gui/container/anvil.png'
|
|
110
|
+
return (_map[path] as string | undefined) ?? path
|
|
111
|
+
},
|
|
112
|
+
}
|
package/src/globals.d.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -14,7 +14,8 @@ export { ItemCanvas } from './components/ItemCanvas'
|
|
|
14
14
|
export { Tooltip } from './components/Tooltip'
|
|
15
15
|
export { JEI } from './components/JEI'
|
|
16
16
|
export type { JEIItem } from './components/JEI'
|
|
17
|
-
export {
|
|
17
|
+
export { Notes } from './components/Notes'
|
|
18
|
+
export type { Note } from './components/Notes'
|
|
18
19
|
export { HUD } from './components/HUD'
|
|
19
20
|
export { CursorItem } from './components/CursorItem'
|
|
20
21
|
export { InventoryOverlay } from './components/InventoryOverlay'
|
|
@@ -46,7 +47,7 @@ export type { ActionLogEntry, DemoConnectorOptions } from './connector/demo'
|
|
|
46
47
|
|
|
47
48
|
// Registry
|
|
48
49
|
export { registerInventoryType, getInventoryType, getAllInventoryTypes } from './registry'
|
|
49
|
-
export type { InventoryTypeDefinition } from './registry'
|
|
50
|
+
export type { InventoryTypeDefinition, WindowType } from './registry'
|
|
50
51
|
|
|
51
52
|
// Types
|
|
52
53
|
export type {
|
package/src/registry/index.ts
CHANGED