Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PrismarineJS
GitHub Repository: PrismarineJS/mineflayer
Path: blob/master/lib/plugins/bed.js
1467 views
1
const { Vec3 } = require('vec3')
2
3
module.exports = inject
4
5
const CARDINAL_DIRECTIONS = ['south', 'west', 'north', 'east']
6
7
function inject (bot) {
8
bot.isSleeping = false
9
10
const beds = new Set(['white_bed', 'orange_bed', 'magenta_bed', 'light_blue_bed', 'yellow_bed', 'lime_bed', 'pink_bed', 'gray_bed',
11
'light_gray_bed', 'cyan_bed', 'purple_bed', 'blue_bed', 'brown_bed', 'green_bed', 'red_bed', 'black_bed', 'bed'])
12
13
function isABed (block) {
14
return beds.has(block.name)
15
}
16
17
function parseBedMetadata (bedBlock) {
18
const metadata = {
19
part: false, // true: head, false: foot
20
occupied: 0,
21
facing: 0, // 0: south, 1: west, 2: north, 3 east
22
headOffset: new Vec3(0, 0, 1)
23
}
24
25
if (bot.supportFeature('blockStateId')) {
26
const state = bedBlock.stateId - bot.registry.blocksByStateId[bedBlock.stateId].minStateId
27
const bitMetadata = state.toString(2).padStart(4, '0') // FACING (first 2 bits), PART (3rd bit), OCCUPIED (4th bit)
28
metadata.part = bitMetadata[3] === '0'
29
metadata.occupied = bitMetadata[2] === '0'
30
31
switch (bitMetadata.slice(0, 2)) {
32
case '00':
33
metadata.facing = 2
34
metadata.headOffset.set(0, 0, -1)
35
break
36
case '10':
37
metadata.facing = 1
38
metadata.headOffset.set(-1, 0, 0)
39
break
40
case '11':
41
metadata.facing = 3
42
metadata.headOffset.set(1, 0, 0)
43
}
44
} else if (bot.supportFeature('blockMetadata')) {
45
const bitMetadata = bedBlock.metadata.toString(2).padStart(4, '0') // PART (1st bit), OCCUPIED (2nd bit), FACING (last 2 bits)
46
metadata.part = bitMetadata[0] === '1'
47
metadata.occupied = bitMetadata[1] === '1'
48
49
switch (bitMetadata.slice(2, 4)) {
50
case '01':
51
metadata.facing = 1
52
metadata.headOffset.set(-1, 0, 0)
53
break
54
case '10':
55
metadata.facing = 2
56
metadata.headOffset.set(0, 0, -1)
57
break
58
case '11':
59
metadata.facing = 3
60
metadata.headOffset.set(1, 0, 0)
61
}
62
}
63
64
return metadata
65
}
66
67
async function wake () {
68
if (!bot.isSleeping) {
69
throw new Error('already awake')
70
} else {
71
bot._client.write('entity_action', {
72
entityId: bot.entity.id,
73
actionId: 2,
74
jumpBoost: 0
75
})
76
}
77
}
78
79
async function sleep (bedBlock) {
80
const thunderstorm = bot.isRaining && (bot.thunderState > 0)
81
if (!thunderstorm && !(bot.time.timeOfDay >= 12541 && bot.time.timeOfDay <= 23458)) {
82
throw new Error("it's not night and it's not a thunderstorm")
83
} else if (bot.isSleeping) {
84
throw new Error('already sleeping')
85
} else if (!isABed(bedBlock)) {
86
throw new Error('wrong block : not a bed block')
87
} else {
88
const botPos = bot.entity.position.floored()
89
const metadata = parseBedMetadata(bedBlock)
90
let headPoint = bedBlock.position
91
92
if (metadata.occupied) {
93
throw new Error('the bed is occupied')
94
}
95
96
if (!metadata.part) { // Is foot
97
const upperBlock = bot.blockAt(bedBlock.position.plus(metadata.headOffset))
98
99
if (isABed(upperBlock)) {
100
headPoint = upperBlock.position
101
} else {
102
const lowerBlock = bot.blockAt(bedBlock.position.plus(metadata.headOffset.scaled(-1)))
103
104
if (isABed(lowerBlock)) {
105
// If there are 2 foot parts, minecraft only lets you sleep if you click on the lower one
106
headPoint = bedBlock.position
107
bedBlock = lowerBlock
108
} else {
109
throw new Error("there's only half bed")
110
}
111
}
112
}
113
114
if (!bot.canDigBlock(bedBlock)) {
115
throw new Error('cant click the bed')
116
}
117
118
const clickRange = [2, -3, -3, 2] // [south, west, north, east]
119
const monsterRange = [7, -8, -8, 7]
120
const oppositeCardinal = (metadata.facing + 2) % CARDINAL_DIRECTIONS.length
121
122
if (clickRange[oppositeCardinal] < 0) {
123
clickRange[oppositeCardinal]--
124
} else {
125
clickRange[oppositeCardinal]++
126
}
127
128
const nwClickCorner = headPoint.offset(clickRange[1], -2, clickRange[2]) // North-West lower corner
129
const seClickCorner = headPoint.offset(clickRange[3], 2, clickRange[0]) // South-East upper corner
130
if (botPos.x > seClickCorner.x || botPos.x < nwClickCorner.x || botPos.y > seClickCorner.y || botPos.y < nwClickCorner.y || botPos.z > seClickCorner.z || botPos.z < nwClickCorner.z) {
131
throw new Error('the bed is too far')
132
}
133
134
if (bot.game.gameMode !== 'creative' || bot.supportFeature('creativeSleepNearMobs')) { // If in creative mode the bot should be able to sleep even if there are monster nearby (starting in 1.13)
135
const nwMonsterCorner = headPoint.offset(monsterRange[1], -6, monsterRange[2]) // North-West lower corner
136
const seMonsterCorner = headPoint.offset(monsterRange[3], 4, monsterRange[0]) // South-East upper corner
137
138
for (const key of Object.keys(bot.entities)) {
139
const entity = bot.entities[key]
140
if (entity.kind === 'Hostile mobs') {
141
const entityPos = entity.position.floored()
142
if (entityPos.x <= seMonsterCorner.x && entityPos.x >= nwMonsterCorner.x && entityPos.y <= seMonsterCorner.y && entityPos.y >= nwMonsterCorner.y && entityPos.z <= seMonsterCorner.z && entityPos.z >= nwMonsterCorner.z) {
143
throw new Error('there are monsters nearby')
144
}
145
}
146
}
147
}
148
149
bot.activateBlock(bedBlock)
150
151
await waitUntilSleep()
152
}
153
}
154
155
async function waitUntilSleep () {
156
return new Promise((resolve, reject) => {
157
const timeoutForSleep = setTimeout(() => {
158
reject(new Error('bot is not sleeping'))
159
}, 3000)
160
161
bot.once('sleep', () => {
162
clearTimeout(timeoutForSleep)
163
resolve()
164
})
165
})
166
}
167
168
bot._client.on('game_state_change', (packet) => {
169
if (packet.reason === 0) {
170
// occurs when you can't spawn in your bed and your spawn point gets reset
171
bot.emit('spawnReset')
172
}
173
})
174
175
bot.on('entitySleep', (entity) => {
176
if (entity === bot.entity) {
177
bot.isSleeping = true
178
bot.emit('sleep')
179
}
180
})
181
182
bot.on('entityWake', (entity) => {
183
if (entity === bot.entity) {
184
bot.isSleeping = false
185
bot.emit('wake')
186
}
187
})
188
189
bot.parseBedMetadata = parseBedMetadata
190
bot.wake = wake
191
bot.sleep = sleep
192
bot.isABed = isABed
193
}
194
195