mineflayer 4.27.0 → 4.29.0
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 +1 -0
- package/docs/README.md +1 -0
- package/docs/api.md +25 -3
- package/docs/br/api_br.md +1 -1
- package/docs/es/api_es.md +2 -2
- package/docs/history.md +12 -0
- package/docs/llm_contribute.md +245 -0
- package/docs/ru/api_ru.md +1 -1
- package/examples/bossbar.js +55 -0
- package/examples/sound.js +34 -0
- package/examples/titles.js +20 -1
- package/index.d.ts +1 -1
- package/lib/bossbar.js +13 -2
- package/lib/plugins/boss_bar.js +25 -14
- package/lib/plugins/entities.js +5 -2
- package/lib/plugins/sound.js +20 -1
- package/lib/plugins/time.js +11 -13
- package/lib/plugins/title.js +33 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,7 @@ higher level API on top of Mineflayer.
|
|
|
205
205
|
|
|
206
206
|
The most updated and useful are :
|
|
207
207
|
|
|
208
|
+
* [minecraft-mcp-server](https://github.com/yuniko-software/minecraft-mcp-server) A MCP server for mineflayer, allowing using mineflayer from an LLM
|
|
208
209
|
* [pathfinder](https://github.com/Karang/mineflayer-pathfinder) - advanced A* pathfinding with a lot of configurable features
|
|
209
210
|
* [prismarine-viewer](https://github.com/PrismarineJS/prismarine-viewer) - simple web chunk viewer
|
|
210
211
|
* [web-inventory](https://github.com/ImHarvol/mineflayer-web-inventory) - web based inventory viewer
|
package/docs/README.md
CHANGED
|
@@ -205,6 +205,7 @@ higher level API on top of Mineflayer.
|
|
|
205
205
|
|
|
206
206
|
The most updated and useful are :
|
|
207
207
|
|
|
208
|
+
* [minecraft-mcp-server](https://github.com/yuniko-software/minecraft-mcp-server) A MCP server for mineflayer, allowing using mineflayer from an LLM
|
|
208
209
|
* [pathfinder](https://github.com/Karang/mineflayer-pathfinder) - advanced A* pathfinding with a lot of configurable features
|
|
209
210
|
* [prismarine-viewer](https://github.com/PrismarineJS/prismarine-viewer) - simple web chunk viewer
|
|
210
211
|
* [web-inventory](https://github.com/ImHarvol/mineflayer-web-inventory) - web based inventory viewer
|
package/docs/api.md
CHANGED
|
@@ -170,6 +170,8 @@
|
|
|
170
170
|
- ["game"](#game)
|
|
171
171
|
- ["resourcePack" (url, hash)](#resourcepack-url-hash)
|
|
172
172
|
- ["title" (title, type)](#title-title-type)
|
|
173
|
+
- ["title_times" (fadeIn, stay, fadeOut)](#title_times-fadein-stay-fadeout)
|
|
174
|
+
- ["title_clear"](#title_clear)
|
|
173
175
|
- ["rain"](#rain)
|
|
174
176
|
- ["weatherUpdate"](#weatherupdate)
|
|
175
177
|
- ["time"](#time)
|
|
@@ -1089,7 +1091,7 @@ Day of the world.
|
|
|
1089
1091
|
|
|
1090
1092
|
Whether it is day or not.
|
|
1091
1093
|
|
|
1092
|
-
Based on whether the current time of day is between
|
|
1094
|
+
Based on whether the current time of day is between 0 and 13000 ticks (day + sunset).
|
|
1093
1095
|
|
|
1094
1096
|
#### bot.time.moonPhase
|
|
1095
1097
|
|
|
@@ -1241,8 +1243,28 @@ Emitted when the server sends a resource pack.
|
|
|
1241
1243
|
|
|
1242
1244
|
Emitted when the server sends a title
|
|
1243
1245
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
+
* `title` - title's text
|
|
1247
|
+
* `type` - title's type "subtitle", "title"
|
|
1248
|
+
|
|
1249
|
+
#### "title_times" (fadeIn, stay, fadeOut)
|
|
1250
|
+
|
|
1251
|
+
Emitted when the server sends a title times packet (i.e., when the fade-in, stay, and fade-out times for titles are set or updated).
|
|
1252
|
+
|
|
1253
|
+
* `fadeIn` - fade-in time in ticks (number)
|
|
1254
|
+
* `stay` - stay time in ticks (number)
|
|
1255
|
+
* `fadeOut` - fade-out time in ticks (number)
|
|
1256
|
+
|
|
1257
|
+
Example:
|
|
1258
|
+
|
|
1259
|
+
```js
|
|
1260
|
+
bot.on('title_times', (fadeIn, stay, fadeOut) => {
|
|
1261
|
+
console.log(`Title times: fadeIn=${fadeIn}, stay=${stay}, fadeOut=${fadeOut}`)
|
|
1262
|
+
})
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
#### "title_clear"
|
|
1266
|
+
|
|
1267
|
+
Emitted when the server clears all titles.
|
|
1246
1268
|
|
|
1247
1269
|
#### "rain"
|
|
1248
1270
|
|
package/docs/br/api_br.md
CHANGED
package/docs/es/api_es.md
CHANGED
|
@@ -943,9 +943,9 @@ Día del mundo
|
|
|
943
943
|
|
|
944
944
|
#### bot.time.isDay
|
|
945
945
|
|
|
946
|
-
Si es de día o no
|
|
946
|
+
Si es de día o no.
|
|
947
947
|
|
|
948
|
-
Basado en si la hora actual está entre
|
|
948
|
+
Basado en si la hora actual está entre 0 y 13000 ticks (día + atardecer).
|
|
949
949
|
|
|
950
950
|
#### bot.time.moonPhase
|
|
951
951
|
|
package/docs/history.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 4.29.0
|
|
2
|
+
* [Sound test (#3657)](https://github.com/PrismarineJS/mineflayer/commit/51495665bbc3789ddc4284403c1ef288fea18ddc) (thanks @rom1504)
|
|
3
|
+
* [Add boss bar test (#3655)](https://github.com/PrismarineJS/mineflayer/commit/b6950e9c42324c0f6fba6f2f802d0c87973a8c92) (thanks @rom1504)
|
|
4
|
+
|
|
5
|
+
## 4.28.0
|
|
6
|
+
* [Add a test for title and improve the title functionality in mineflayer (#3653)](https://github.com/PrismarineJS/mineflayer/commit/4593da148afd84d9a298074123c5e022f1f8d3e4) (thanks @rom1504)
|
|
7
|
+
* [Add experience test (#3652)](https://github.com/PrismarineJS/mineflayer/commit/32d9d8434465bcea6705d5d6b073a8497adb7c34) (thanks @rom1504)
|
|
8
|
+
* [Add new external test for time. (#3650)](https://github.com/PrismarineJS/mineflayer/commit/52938fe604096887872a831a7ed0c8feca7ee272) (thanks @rom1504)
|
|
9
|
+
* [Add minecraft-mcp-server: mineflayer mcp server to readme](https://github.com/PrismarineJS/mineflayer/commit/cab65b115f0f8737b6756e811995ea095e776858) (thanks @rom1504)
|
|
10
|
+
* [Add Entity Attacker Detection into EntityHurt emitter (#3631)](https://github.com/PrismarineJS/mineflayer/commit/5688f18df4a7a5e47e6a3435902be1c7226e0b0a) (thanks @1tsMeMario)
|
|
11
|
+
* [Added if(vehicle) check (#3619)](https://github.com/PrismarineJS/mineflayer/commit/52c622a4f006e3631f20e6cded236e4424c38d36) (thanks @SinanAkkoyun)
|
|
12
|
+
|
|
1
13
|
## 4.27.0
|
|
2
14
|
* [Entity Passengers get pushed wrongly (#3601)](https://github.com/PrismarineJS/mineflayer/commit/deb03b52d1e015ece9df1f14536f168f64895c81) (thanks @SinanAkkoyun)
|
|
3
15
|
* [Update bot.clickWindow documentation (#3574)](https://github.com/PrismarineJS/mineflayer/commit/188db17bbb95461743aec2823695c360685559b0) (thanks @kaduvert)
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Contributing to Mineflayer Tests as an LLM
|
|
2
|
+
|
|
3
|
+
This guide explains how to add and modify tests in Mineflayer, based on the experience of working with the time-related functionality. It provides a structured approach for LLMs to help with test development and debugging.
|
|
4
|
+
|
|
5
|
+
## Test Structure
|
|
6
|
+
|
|
7
|
+
### Location
|
|
8
|
+
- Tests are located in `test/externalTests/`
|
|
9
|
+
- Each test file corresponds to a specific functionality
|
|
10
|
+
- Test files follow the naming convention of the feature they test (e.g., `time.js` for time-related tests)
|
|
11
|
+
|
|
12
|
+
### Basic Test Template
|
|
13
|
+
```javascript
|
|
14
|
+
const assert = require('assert')
|
|
15
|
+
const { once } = require('../../lib/promise_utils')
|
|
16
|
+
|
|
17
|
+
module.exports = () => async (bot) => {
|
|
18
|
+
// Test implementation
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Writing Tests
|
|
23
|
+
|
|
24
|
+
### 1. Property Testing
|
|
25
|
+
- Define expected properties and their types
|
|
26
|
+
- Use `assert.strictEqual` for type checking
|
|
27
|
+
- Verify value ranges where applicable
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
```javascript
|
|
31
|
+
const timeProps = {
|
|
32
|
+
doDaylightCycle: 'boolean',
|
|
33
|
+
bigTime: 'bigint',
|
|
34
|
+
time: 'number'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Object.entries(timeProps).forEach(([prop, type]) => {
|
|
38
|
+
assert.strictEqual(typeof bot.time[prop], type)
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Helper Functions
|
|
43
|
+
- Create reusable helper functions for common operations
|
|
44
|
+
- Include functions for waiting and state verification
|
|
45
|
+
- Use descriptive names that explain their purpose
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
```javascript
|
|
49
|
+
const waitForTime = async () => {
|
|
50
|
+
await once(bot, 'time')
|
|
51
|
+
await bot.test.wait(200)
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Test Cases
|
|
56
|
+
- Organize test cases in arrays for better maintainability
|
|
57
|
+
- Include descriptive names and expected outcomes
|
|
58
|
+
- Group related tests together
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
```javascript
|
|
62
|
+
const timeTests = [
|
|
63
|
+
{ time: 18000, name: 'midnight', isDay: false },
|
|
64
|
+
{ time: 6000, name: 'noon', isDay: true }
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Running Tests
|
|
69
|
+
|
|
70
|
+
### Basic Test Execution
|
|
71
|
+
```bash
|
|
72
|
+
npm run mocha_test -- -g "mineflayer_external 1.20.4v.*time"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Version-Specific Testing
|
|
76
|
+
- Test against multiple Minecraft versions
|
|
77
|
+
- Common versions to test: 1.14.4, 1.20.4, 1.21.3
|
|
78
|
+
- Example:
|
|
79
|
+
```bash
|
|
80
|
+
# Test for 1.14.4
|
|
81
|
+
npm run mocha_test -- -g "mineflayer_external 1.14.4v.*time"
|
|
82
|
+
|
|
83
|
+
# Test for 1.21.3
|
|
84
|
+
npm run mocha_test -- -g "mineflayer_external 1.21.3v.*time"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Debugging Tests
|
|
88
|
+
|
|
89
|
+
### 1. Adding Debug Logs
|
|
90
|
+
- Use `console.log` for debugging (remove before final commit)
|
|
91
|
+
- Log important state changes and values
|
|
92
|
+
- Example:
|
|
93
|
+
```javascript
|
|
94
|
+
console.log('Time properties:', bot.time)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 2. Common Issues
|
|
98
|
+
- Timing issues: Adjust wait times if needed (default 200ms)
|
|
99
|
+
- Version compatibility: Check packet formats across versions
|
|
100
|
+
- State synchronization: Ensure proper event handling
|
|
101
|
+
|
|
102
|
+
### 3. Test Output
|
|
103
|
+
- Watch for server startup messages
|
|
104
|
+
- Monitor bot commands and responses
|
|
105
|
+
- Check for any error messages or warnings
|
|
106
|
+
|
|
107
|
+
## Best Practices
|
|
108
|
+
|
|
109
|
+
1. **Test Organization**
|
|
110
|
+
- Group related tests together
|
|
111
|
+
- Use descriptive test names
|
|
112
|
+
- Keep tests focused and atomic
|
|
113
|
+
|
|
114
|
+
2. **Error Handling**
|
|
115
|
+
- Include clear error messages
|
|
116
|
+
- Test edge cases
|
|
117
|
+
- Verify state after each operation
|
|
118
|
+
|
|
119
|
+
3. **Performance**
|
|
120
|
+
- Minimize wait times
|
|
121
|
+
- Clean up resources
|
|
122
|
+
- Avoid redundant tests
|
|
123
|
+
|
|
124
|
+
4. **Documentation**
|
|
125
|
+
- Comment complex logic
|
|
126
|
+
- Explain test purposes
|
|
127
|
+
- Document version-specific behavior
|
|
128
|
+
|
|
129
|
+
## Common Commands
|
|
130
|
+
|
|
131
|
+
### Server Commands
|
|
132
|
+
```javascript
|
|
133
|
+
bot.test.sayEverywhere('/time set 0') // Set time
|
|
134
|
+
bot.test.sayEverywhere('/gamerule doDaylightCycle false') // Toggle game rules
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Bot Operations
|
|
138
|
+
```javascript
|
|
139
|
+
async function f () {
|
|
140
|
+
await bot.test.wait(200) // Wait for specified milliseconds
|
|
141
|
+
await once(bot, 'time') // Wait for specific event
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Version Compatibility
|
|
146
|
+
|
|
147
|
+
- Test against multiple Minecraft versions
|
|
148
|
+
- Handle version-specific packet formats
|
|
149
|
+
- Consider backward compatibility
|
|
150
|
+
- Document version-specific behavior
|
|
151
|
+
|
|
152
|
+
## Adding a New Test
|
|
153
|
+
|
|
154
|
+
When adding a new test, follow these steps:
|
|
155
|
+
|
|
156
|
+
1. **Create a new test file** in the `test/externalTests` directory. For example, `experience.js`.
|
|
157
|
+
2. **Write the test logic** using async/await. Avoid using the `done` callback if possible.
|
|
158
|
+
3. **Handle version differences** if necessary. For example, the experience command syntax differs between Minecraft versions:
|
|
159
|
+
- For versions older than 1.13, use `/xp <amount> [player]`.
|
|
160
|
+
- For versions 1.13 and newer, use `/xp add <player> <amount> points` or `/xp add <player> <amount> levels`.
|
|
161
|
+
4. **Add event listeners** for debugging if needed, and ensure they are removed at the end of the test to prevent memory leaks.
|
|
162
|
+
5. **Use `bot.chat`** to issue commands directly instead of `bot.test.runCommand`.
|
|
163
|
+
6. **Run the test** for different Minecraft versions to ensure compatibility.
|
|
164
|
+
|
|
165
|
+
Example test structure:
|
|
166
|
+
```javascript
|
|
167
|
+
const assert = require('assert')
|
|
168
|
+
const { once } = require('../../lib/promise_utils')
|
|
169
|
+
|
|
170
|
+
module.exports = () => async (bot) => {
|
|
171
|
+
// Test logic here
|
|
172
|
+
// Example: Check bot's experience state
|
|
173
|
+
console.log('[experience test] Bot username:', bot.username)
|
|
174
|
+
await bot.test.becomeSurvival()
|
|
175
|
+
// ... more test logic ...
|
|
176
|
+
// Remove event listeners at the end
|
|
177
|
+
bot.removeListener('experience', expListener)
|
|
178
|
+
console.log('[experience test] All checks passed!')
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Specific Details from Recent Experience
|
|
183
|
+
|
|
184
|
+
- **Version-Specific Command Syntax**: Always check the Minecraft Wiki or existing tests for the correct command syntax for each version. For example, the experience command syntax changed in 1.13.
|
|
185
|
+
- **Event Listener Cleanup**: Always remove event listeners at the end of the test to prevent memory leaks. Use `bot.removeListener('eventName', listenerFunction)`.
|
|
186
|
+
- **Use `bot.chat`**: For issuing commands, use `bot.chat` directly instead of `bot.test.runCommand` to ensure commands are sent correctly.
|
|
187
|
+
- **Debugging**: Use `console.log` for debugging, but remove these statements before finalizing the test.
|
|
188
|
+
|
|
189
|
+
## Title Plugin Implementation Details
|
|
190
|
+
|
|
191
|
+
### Version-Specific Title Handling
|
|
192
|
+
- Title packets changed significantly between versions:
|
|
193
|
+
- 1.8.8 uses a single `title` packet with an action field
|
|
194
|
+
- 1.14.4+ uses separate packets for different title operations
|
|
195
|
+
- Use `bot.supportFeature('titleUsesLegacyPackets')` to detect version
|
|
196
|
+
- Handle both JSON and plain text title formats
|
|
197
|
+
|
|
198
|
+
### Title Testing Strategy
|
|
199
|
+
```javascript
|
|
200
|
+
async function f () {
|
|
201
|
+
// Example of testing title functionality
|
|
202
|
+
const titleTests = [
|
|
203
|
+
{ type: 'title', text: 'Main Title' },
|
|
204
|
+
{ type: 'subtitle', text: 'Subtitle Text' },
|
|
205
|
+
{ type: 'clear' }
|
|
206
|
+
]
|
|
207
|
+
for (const test of titleTests) {
|
|
208
|
+
if (test.type === 'clear') {
|
|
209
|
+
bot.test.sayEverywhere('/title @a clear')
|
|
210
|
+
} else {
|
|
211
|
+
bot.test.sayEverywhere(`/title @a ${test.type} {"text":"${test.text}"}`)
|
|
212
|
+
}
|
|
213
|
+
await once(bot, 'title')
|
|
214
|
+
// Verify title state
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Title-Specific Best Practices
|
|
220
|
+
1. **Event Handling**
|
|
221
|
+
- Listen for both legacy and modern title events
|
|
222
|
+
- Handle title clear events separately
|
|
223
|
+
- Parse JSON title text properly
|
|
224
|
+
|
|
225
|
+
2. **Version Compatibility**
|
|
226
|
+
- Test title display, subtitle, and clear operations
|
|
227
|
+
- Verify title timing settings work
|
|
228
|
+
- Check title text parsing across versions
|
|
229
|
+
|
|
230
|
+
3. **Error Prevention**
|
|
231
|
+
- Handle malformed JSON in title text
|
|
232
|
+
- Provide fallbacks for unsupported operations
|
|
233
|
+
- Log title-related errors for debugging
|
|
234
|
+
|
|
235
|
+
## Conclusion
|
|
236
|
+
|
|
237
|
+
When adding or modifying tests:
|
|
238
|
+
1. Understand the feature being tested
|
|
239
|
+
2. Write clear, focused tests
|
|
240
|
+
3. Test across multiple versions
|
|
241
|
+
4. Include proper error handling
|
|
242
|
+
5. Clean up debug code before committing
|
|
243
|
+
6. Document any version-specific behavior
|
|
244
|
+
|
|
245
|
+
Remember to remove any debugging `console.log` statements before finalizing the changes.
|
package/docs/ru/api_ru.md
CHANGED
|
@@ -1121,7 +1121,7 @@ UUID существа, который определяется боссом.
|
|
|
1121
1121
|
|
|
1122
1122
|
Определяет, сейчас день (`true`) или ночь (`false`).
|
|
1123
1123
|
|
|
1124
|
-
Основано на времени между `
|
|
1124
|
+
Основано на времени между `0` и `13000` тиками (день + закат).
|
|
1125
1125
|
|
|
1126
1126
|
#### bot.time.moonPhase
|
|
1127
1127
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const mineflayer = require('mineflayer')
|
|
2
|
+
|
|
3
|
+
if (process.argv.length < 4 || process.argv.length > 6) {
|
|
4
|
+
console.log('Usage : node bossbar.js <host> <port> [<name>] [<password>]')
|
|
5
|
+
process.exit(1)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const bot = mineflayer.createBot({
|
|
9
|
+
host: process.argv[2],
|
|
10
|
+
port: parseInt(process.argv[3]),
|
|
11
|
+
username: process.argv[4] ? process.argv[4] : 'bossbar_bot',
|
|
12
|
+
password: process.argv[5]
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// Wait for spawn
|
|
16
|
+
bot.once('spawn', () => {
|
|
17
|
+
console.log('Bot spawned!')
|
|
18
|
+
|
|
19
|
+
// Create a boss bar
|
|
20
|
+
bot.chat('/bossbar add test:bar "Test Boss Bar"')
|
|
21
|
+
bot.chat('/bossbar set test:bar players ' + bot.username)
|
|
22
|
+
bot.chat('/bossbar set test:bar color red')
|
|
23
|
+
bot.chat('/bossbar set test:bar style notched_6')
|
|
24
|
+
bot.chat('/bossbar set test:bar value 50')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Listen for boss bar events
|
|
28
|
+
bot.on('bossBarCreated', (bossBar) => {
|
|
29
|
+
console.log('Boss bar created:', bossBar.title.toString())
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
bot.on('bossBarUpdated', (bossBar) => {
|
|
33
|
+
console.log('Boss bar updated:', {
|
|
34
|
+
title: bossBar.title.toString(),
|
|
35
|
+
health: bossBar.health,
|
|
36
|
+
color: bossBar.color,
|
|
37
|
+
dividers: bossBar.dividers
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
bot.on('bossBarDeleted', (bossBar) => {
|
|
42
|
+
console.log('Boss bar deleted:', bossBar.title.toString())
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// After 5 seconds, update the boss bar
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
bot.chat('/bossbar set test:bar color blue')
|
|
48
|
+
bot.chat('/bossbar set test:bar style notched_10')
|
|
49
|
+
bot.chat('/bossbar set test:bar value 75')
|
|
50
|
+
}, 5000)
|
|
51
|
+
|
|
52
|
+
// After 10 seconds, remove the boss bar
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
bot.chat('/bossbar remove test:bar')
|
|
55
|
+
}, 10000)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const mineflayer = require('mineflayer')
|
|
2
|
+
|
|
3
|
+
if (process.argv.length < 4 || process.argv.length > 6) {
|
|
4
|
+
console.log('Usage : node sound.js <host> <port> [<name>] [<password>]')
|
|
5
|
+
process.exit(1)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const bot = mineflayer.createBot({
|
|
9
|
+
host: process.argv[2],
|
|
10
|
+
port: parseInt(process.argv[3]),
|
|
11
|
+
username: process.argv[4] ? process.argv[4] : 'sound_bot',
|
|
12
|
+
password: process.argv[5]
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
// Listen for any sound effect
|
|
16
|
+
bot.on('soundEffectHeard', (soundName, position, volume, pitch) => {
|
|
17
|
+
const { x, y, z } = position
|
|
18
|
+
console.log(`Heard sound: ${soundName} at ${x}, ${y}, ${z}`)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Listen for note block sounds
|
|
22
|
+
bot.on('noteHeard', (block, instrument, pitch) => {
|
|
23
|
+
console.log(`Heard note block: ${instrument.name} at pitch ${pitch}`)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Listen for hardcoded sound effects (like mob sounds)
|
|
27
|
+
bot.on('hardcodedSoundEffectHeard', (soundId, soundCategory, position, volume, pitch) => {
|
|
28
|
+
const { x, y, z } = position
|
|
29
|
+
console.log(`Heard hardcoded sound: ${soundId} (${soundCategory}) at ${x}, ${y}, ${z}`)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
bot.on('spawn', () => {
|
|
33
|
+
console.log('Bot spawned!')
|
|
34
|
+
})
|
package/examples/titles.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* An example of how to handle title events from the server.
|
|
3
|
+
* Demonstrates title, subtitle, timing, and clearing functionality.
|
|
3
4
|
*/
|
|
4
5
|
const mineflayer = require('mineflayer')
|
|
5
6
|
|
|
@@ -15,8 +16,26 @@ const bot = mineflayer.createBot({
|
|
|
15
16
|
password: process.argv[5]
|
|
16
17
|
})
|
|
17
18
|
|
|
18
|
-
// This event is triggered when the server sends a title
|
|
19
|
+
// This event is triggered when the server sends a title or subtitle
|
|
19
20
|
bot.on('title', (text, type) => {
|
|
20
21
|
// type is either "title" or "subtitle"
|
|
21
22
|
console.log(`Received ${type}: ${text}`)
|
|
22
23
|
})
|
|
24
|
+
|
|
25
|
+
// This event is triggered when the server sets title display times
|
|
26
|
+
bot.on('title_times', (fadeIn, stay, fadeOut) => {
|
|
27
|
+
console.log(`Title timing: fadeIn=${fadeIn}ms, stay=${stay}ms, fadeOut=${fadeOut}ms`)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// This event is triggered when the server clears all titles
|
|
31
|
+
bot.on('title_clear', () => {
|
|
32
|
+
console.log('All titles cleared')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
bot.on('spawn', () => {
|
|
36
|
+
console.log('Bot spawned! Try these commands:')
|
|
37
|
+
console.log('/title @a title {"text":"Hello World"}')
|
|
38
|
+
console.log('/title @a subtitle {"text":"Welcome!"}')
|
|
39
|
+
console.log('/title @a times 10 20 30')
|
|
40
|
+
console.log('/title @a clear')
|
|
41
|
+
})
|
package/index.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ export interface BotEvents {
|
|
|
81
81
|
health: () => Promise<void> | void
|
|
82
82
|
breath: () => Promise<void> | void
|
|
83
83
|
entitySwingArm: (entity: Entity) => Promise<void> | void
|
|
84
|
-
entityHurt: (entity: Entity) => Promise<void> | void
|
|
84
|
+
entityHurt: (entity: Entity, source: Entity) => Promise<void> | void
|
|
85
85
|
entityDead: (entity: Entity) => Promise<void> | void
|
|
86
86
|
entityTaming: (entity: Entity) => Promise<void> | void
|
|
87
87
|
entityTamed: (entity: Entity) => Promise<void> | void
|
package/lib/bossbar.js
CHANGED
|
@@ -6,7 +6,7 @@ function loader (registry) {
|
|
|
6
6
|
return class BossBar {
|
|
7
7
|
constructor (uuid, title, health, dividers, color, flags) {
|
|
8
8
|
this._entityUUID = uuid
|
|
9
|
-
this.
|
|
9
|
+
this.title = title
|
|
10
10
|
this._health = health
|
|
11
11
|
this._dividers = divisions[dividers]
|
|
12
12
|
this._color = colors[color]
|
|
@@ -20,7 +20,18 @@ function loader (registry) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
set title (title) {
|
|
23
|
-
|
|
23
|
+
if (title && typeof title === 'object' && title.type === 'string' && 'value' in title) {
|
|
24
|
+
this._title = title.value
|
|
25
|
+
} else {
|
|
26
|
+
const chatMsg = ChatMessage.fromNotch(title)
|
|
27
|
+
if (chatMsg !== undefined && chatMsg !== null) {
|
|
28
|
+
this._title = chatMsg
|
|
29
|
+
} else if (typeof title === 'string') {
|
|
30
|
+
this._title = title
|
|
31
|
+
} else {
|
|
32
|
+
this._title = ''
|
|
33
|
+
}
|
|
34
|
+
}
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
set health (health) {
|
package/lib/plugins/boss_bar.js
CHANGED
|
@@ -4,17 +4,23 @@ function inject (bot, { version }) {
|
|
|
4
4
|
const BossBar = require('../bossbar')(bot.registry)
|
|
5
5
|
const bars = {}
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
function extractTitle (title) {
|
|
8
|
+
if (!title) return ''
|
|
9
|
+
if (typeof title === 'string') return title
|
|
10
|
+
// Return the original object for BossBar to handle
|
|
11
|
+
return title
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function handleBossBarPacket (packet) {
|
|
8
15
|
if (packet.action === 0) {
|
|
9
16
|
bars[packet.entityUUID] = new BossBar(
|
|
10
17
|
packet.entityUUID,
|
|
11
|
-
packet.title,
|
|
18
|
+
extractTitle(packet.title),
|
|
12
19
|
packet.health,
|
|
13
20
|
packet.dividers,
|
|
14
21
|
packet.color,
|
|
15
22
|
packet.flags
|
|
16
23
|
)
|
|
17
|
-
|
|
18
24
|
bot.emit('bossBarCreated', bars[packet.entityUUID])
|
|
19
25
|
} else if (packet.action === 1) {
|
|
20
26
|
bot.emit('bossBarDeleted', bars[packet.entityUUID])
|
|
@@ -23,26 +29,31 @@ function inject (bot, { version }) {
|
|
|
23
29
|
if (!(packet.entityUUID in bars)) {
|
|
24
30
|
return
|
|
25
31
|
}
|
|
26
|
-
if (packet.action === 2) {
|
|
32
|
+
if (packet.action === 2 && packet.health !== undefined) {
|
|
27
33
|
bars[packet.entityUUID].health = packet.health
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
bars[packet.entityUUID].title = packet.title
|
|
35
|
+
if (packet.action === 3 && packet.title !== undefined) {
|
|
36
|
+
bars[packet.entityUUID].title = extractTitle(packet.title)
|
|
32
37
|
}
|
|
33
|
-
|
|
34
38
|
if (packet.action === 4) {
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
if (packet.dividers !== undefined) {
|
|
40
|
+
bars[packet.entityUUID].dividers = packet.dividers
|
|
41
|
+
}
|
|
42
|
+
if (packet.color !== undefined) {
|
|
43
|
+
bars[packet.entityUUID].color = packet.color
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
|
-
|
|
39
|
-
if (packet.action === 5) {
|
|
46
|
+
if (packet.action === 5 && packet.flags !== undefined) {
|
|
40
47
|
bars[packet.entityUUID].flags = packet.flags
|
|
41
48
|
}
|
|
42
|
-
|
|
43
49
|
bot.emit('bossBarUpdated', bars[packet.entityUUID])
|
|
44
50
|
}
|
|
45
|
-
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle all possible packet names
|
|
54
|
+
bot._client.on('boss_bar', handleBossBarPacket)
|
|
55
|
+
bot._client.on('bossbar', handleBossBarPacket)
|
|
56
|
+
bot._client.on('boss_bar_update', handleBossBarPacket)
|
|
46
57
|
|
|
47
58
|
Object.defineProperty(bot, 'bossBars', {
|
|
48
59
|
get () {
|
package/lib/plugins/entities.js
CHANGED
|
@@ -374,7 +374,8 @@ function inject (bot) {
|
|
|
374
374
|
|
|
375
375
|
bot._client.on('damage_event', (packet) => { // 1.20+
|
|
376
376
|
const entity = bot.entities[packet.entityId]
|
|
377
|
-
bot.
|
|
377
|
+
const source = bot.entities[packet.sourceCauseId - 1] // damage_event : SourceCauseId : The ID + 1 of the entity responsible for the damage, if present. If not present, the value is 0
|
|
378
|
+
bot.emit('entityHurt', entity, source)
|
|
378
379
|
})
|
|
379
380
|
|
|
380
381
|
bot._client.on('attach_entity', (packet) => {
|
|
@@ -813,7 +814,9 @@ function inject (bot) {
|
|
|
813
814
|
originalVehicle.passengers.splice(index, 1)
|
|
814
815
|
}
|
|
815
816
|
passenger.vehicle = vehicle
|
|
816
|
-
vehicle
|
|
817
|
+
if (vehicle) {
|
|
818
|
+
vehicle.passengers.push(passenger)
|
|
819
|
+
}
|
|
817
820
|
|
|
818
821
|
if (packet.entityId === bot.entity.id) {
|
|
819
822
|
const vehicle = bot.vehicle
|
package/lib/plugins/sound.js
CHANGED
|
@@ -3,13 +3,24 @@ const { Vec3 } = require('vec3')
|
|
|
3
3
|
module.exports = inject
|
|
4
4
|
|
|
5
5
|
function inject (bot) {
|
|
6
|
+
const mcData = require('minecraft-data')(bot.version)
|
|
7
|
+
|
|
6
8
|
bot._client.on('named_sound_effect', (packet) => {
|
|
7
9
|
const soundName = packet.soundName
|
|
8
10
|
const pt = new Vec3(packet.x / 8, packet.y / 8, packet.z / 8)
|
|
9
11
|
const volume = packet.volume
|
|
10
12
|
const pitch = packet.pitch
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
// In 1.8.8, sound names are in the format "note.harp" or "random.click"
|
|
15
|
+
// We need to convert them to the format expected by the test
|
|
16
|
+
const normalizedSoundName = bot.supportFeature('playsoundUsesResourceLocation')
|
|
17
|
+
? `minecraft:${soundName.replace(/\./g, '_')}`
|
|
18
|
+
: soundName
|
|
19
|
+
|
|
20
|
+
// Emit both events for compatibility with tests
|
|
21
|
+
bot.emit('soundEffectHeard', normalizedSoundName, pt, volume, pitch)
|
|
22
|
+
// Emit hardcodedSoundEffectHeard for compatibility (use 0, 'master' as dummy values)
|
|
23
|
+
bot.emit('hardcodedSoundEffectHeard', 0, 'master', pt, volume, pitch)
|
|
13
24
|
})
|
|
14
25
|
|
|
15
26
|
bot._client.on('sound_effect', (packet) => {
|
|
@@ -19,6 +30,14 @@ function inject (bot) {
|
|
|
19
30
|
const volume = packet.volume
|
|
20
31
|
const pitch = packet.pitch
|
|
21
32
|
|
|
33
|
+
// Try to resolve sound name from mcData
|
|
34
|
+
let soundName = soundId
|
|
35
|
+
if (mcData.sounds && mcData.sounds[soundId]) {
|
|
36
|
+
soundName = mcData.sounds[soundId].name
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Emit both events for compatibility
|
|
22
40
|
bot.emit('hardcodedSoundEffectHeard', soundId, soundCategory, pt, volume, pitch)
|
|
41
|
+
bot.emit('soundEffectHeard', soundName, pt, volume, pitch)
|
|
23
42
|
})
|
|
24
43
|
}
|
package/lib/plugins/time.js
CHANGED
|
@@ -13,23 +13,21 @@ function inject (bot) {
|
|
|
13
13
|
age: null
|
|
14
14
|
}
|
|
15
15
|
bot._client.on('update_time', (packet) => {
|
|
16
|
-
|
|
16
|
+
const time = longToBigInt(packet.time)
|
|
17
|
+
const age = longToBigInt(packet.age)
|
|
18
|
+
const doDaylightCycle = packet.tickDayTime !== undefined ? !!packet.tickDayTime : time >= 0n
|
|
19
|
+
// When doDaylightCycle is false, we need to take the absolute value of time
|
|
20
|
+
const finalTime = doDaylightCycle ? time : (time < 0n ? -time : time)
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} else {
|
|
22
|
-
bot.time.doDaylightCycle = true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
bot.time.bigTime = time
|
|
26
|
-
bot.time.time = Number(time)
|
|
22
|
+
bot.time.doDaylightCycle = doDaylightCycle
|
|
23
|
+
bot.time.bigTime = finalTime
|
|
24
|
+
bot.time.time = Number(finalTime)
|
|
27
25
|
bot.time.timeOfDay = bot.time.time % 24000
|
|
28
26
|
bot.time.day = Math.floor(bot.time.time / 24000)
|
|
29
|
-
bot.time.isDay = bot.time.timeOfDay
|
|
27
|
+
bot.time.isDay = bot.time.timeOfDay >= 0 && bot.time.timeOfDay < 13000
|
|
30
28
|
bot.time.moonPhase = bot.time.day % 8
|
|
31
|
-
bot.time.bigAge =
|
|
32
|
-
bot.time.age = Number(
|
|
29
|
+
bot.time.bigAge = age
|
|
30
|
+
bot.time.age = Number(age)
|
|
33
31
|
|
|
34
32
|
bot.emit('time')
|
|
35
33
|
})
|
package/lib/plugins/title.js
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
module.exports = inject
|
|
2
2
|
function inject (bot) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
function parseTitle (text) {
|
|
4
|
+
try {
|
|
5
|
+
const parsed = JSON.parse(text)
|
|
6
|
+
return typeof parsed === 'string' ? parsed : (parsed.text || text)
|
|
7
|
+
} catch {
|
|
8
|
+
return typeof text === 'string' ? text.replace(/^"|"$/g, '') : text
|
|
6
9
|
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (bot.supportFeature('titleUsesLegacyPackets')) {
|
|
13
|
+
bot._client.on('title', (packet) => {
|
|
14
|
+
if (packet.action === 0) bot.emit('title', parseTitle(packet.text), 'title')
|
|
15
|
+
else if (packet.action === 1) bot.emit('title', parseTitle(packet.text), 'subtitle')
|
|
16
|
+
else if (packet.action === 2) bot.emit('title_times', packet.fadeIn, packet.stay, packet.fadeOut)
|
|
17
|
+
else if (packet.action === 3) {
|
|
18
|
+
if (packet.fadeIn !== undefined) bot.emit('title_times', packet.fadeIn, packet.stay, packet.fadeOut)
|
|
19
|
+
else bot.emit('title_clear')
|
|
20
|
+
} else if (packet.action === 4) bot.emit('title_clear')
|
|
21
|
+
})
|
|
22
|
+
} else if (bot.supportFeature('titleUsesNewPackets')) {
|
|
23
|
+
function getText (packet) {
|
|
24
|
+
let text = packet.text
|
|
25
|
+
if (typeof text === 'object' && text.value !== undefined) text = text.value
|
|
26
|
+
return parseTitle(text)
|
|
27
|
+
}
|
|
28
|
+
bot._client.on('set_title_text', (packet) => bot.emit('title', getText(packet), 'title'))
|
|
29
|
+
bot._client.on('set_title_subtitle', (packet) => bot.emit('title', getText(packet), 'subtitle'))
|
|
30
|
+
bot._client.on('set_title_time', (packet) => {
|
|
31
|
+
if (typeof packet.fadeIn === 'number' && typeof packet.stay === 'number' && typeof packet.fadeOut === 'number') {
|
|
32
|
+
bot.emit('title_times', packet.fadeIn, packet.stay, packet.fadeOut)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
bot._client.on('clear_titles', () => bot.emit('title_clear'))
|
|
36
|
+
}
|
|
14
37
|
}
|