spectra-physics 1.0.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.
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+ // Version switches for client behaviour that differs between 1.16.5 and 26.x.
3
+ // Uses the protocol version number so "1.21.3" (768) == "1.21.2" etc.
4
+
5
+ const PV = {
6
+ '1.16.5': 754,
7
+ '1.17': 755,
8
+ '1.19.4': 762,
9
+ '1.20': 763,
10
+ '1.20.2': 764,
11
+ '1.20.3': 765,
12
+ '1.20.5': 766,
13
+ '1.21': 767,
14
+ '1.21.2': 768,
15
+ '1.21.4': 769,
16
+ '1.21.5': 770,
17
+ '1.21.6': 771,
18
+ '1.21.9': 773
19
+ }
20
+
21
+ function versionFlags (registry) {
22
+ const pv = registry.version['>='] ? registry.version.version : registry.version
23
+ const at = (name) => pv >= PV[name]
24
+ return {
25
+ protocol: pv,
26
+ name: registry.version.minecraftVersion,
27
+ // login / configuration
28
+ configPhase: at('1.20.2'), // brand + client information are sent in the configuration phase
29
+ // movement packets
30
+ movementFlags: at('1.21.2'), // onGround + horizontalCollision bitflags in move packets
31
+ tickEnd: at('1.21.2'), // ServerboundClientTickEndPacket after every client tick
32
+ playerInputPacket: at('1.21.2'), // ServerboundPlayerInputPacket sent on key change (replaces steer_vehicle)
33
+ shiftInInput: at('1.21.6'), // no more PRESS/RELEASE_SHIFT_KEY player commands
34
+ clientLoaded: at('1.21.4'), // player does not tick until the level screen closes, then sends player_loaded
35
+ posRotBeforeConfirm: pv === PV['1.21.2'], // 1.21.2/1.21.3 answer teleports with pos-rot *before* accept-teleportation
36
+ relativeTeleportVelocity: at('1.21.2'), // teleport packet carries delta movement + delta flags
37
+ lpVelocity: at('1.21.9'), // entity_velocity uses lpVec3 (already blocks/tick)
38
+ // physics
39
+ posFromBoundingBox: !at('1.17'), // 1.16 recalculates the position from the bounding box centre
40
+ flyingSpeedLive: at('1.19.4'), // Player#getFlyingSpeed() instead of the lagging flyingSpeed field
41
+ minorCollision: at('1.19.4'), // sprinting survives "minor" horizontal collisions
42
+ supportingBlock: at('1.20'), // mainSupportingBlockPos drives the block below
43
+ attributes2005: at('1.20.5'), // gravity / step_height / jump_strength attributes
44
+ jumpKeepsHigherY: at('1.21'),
45
+ modernStepUp: at('1.21'), // collectCandidateStepUpHeights step algorithm
46
+ normalizedInput: at('1.21.2'), // KeyboardInput#moveVector is normalized
47
+ modernBackOff: at('1.21.2'), // canFallAtLeast based sneak edge back-off
48
+ squareInput: at('1.21.5'), // modifyInputSpeedForSquareMovement
49
+ fallDistanceDouble: at('1.21.5'),
50
+ // vehicles
51
+ moveVehicleOnGround: at('1.21.2')
52
+ }
53
+ }
54
+
55
+ module.exports = { versionFlags, PV }
@@ -0,0 +1,162 @@
1
+ 'use strict'
2
+ // Block / fluid access layer for the vanilla physics port.
3
+ // Reads mineflayer's world (prismarine-world) and caches per-stateId block traits.
4
+
5
+ const { f32 } = require('./mth')
6
+
7
+ const CLIMBABLE = new Set([
8
+ 'ladder', 'vine', 'scaffolding', 'weeping_vines', 'weeping_vines_plant',
9
+ 'twisting_vines', 'twisting_vines_plant', 'cave_vines', 'cave_vines_plant'
10
+ ])
11
+ const WATER_SOURCES = new Set(['seagrass', 'tall_seagrass', 'kelp', 'kelp_plant', 'bubble_column'])
12
+ const FRICTION = { ice: 0.98, packed_ice: 0.98, frosted_ice: 0.98, blue_ice: 0.989, slime_block: 0.8, slime: 0.8 }
13
+ const SPEED_FACTOR = { soul_sand: 0.4, honey_block: 0.4 }
14
+ const JUMP_FACTOR = { honey_block: 0.5 }
15
+
16
+ const FULL = [0, 0, 0, 1, 1, 1]
17
+
18
+ class WorldView {
19
+ constructor (bot) {
20
+ this.bot = bot
21
+ this.registry = bot.registry
22
+ this.Block = require('prismarine-block')(bot.registry)
23
+ this.cache = new Map()
24
+ this.pos = { x: 0, y: 0, z: 0 }
25
+ }
26
+
27
+ get minY () { return this.bot.game?.minY ?? 0 }
28
+ get maxY () { return this.minY + (this.bot.game?.height ?? 256) }
29
+
30
+ hasChunk (bx, bz) {
31
+ return !!this.bot.world.getColumn(Math.floor(bx) >> 4, Math.floor(bz) >> 4)
32
+ }
33
+
34
+ // returns null when the column is not loaded
35
+ stateAt (x, y, z) {
36
+ const col = this.bot.world.getColumn(x >> 4, z >> 4)
37
+ if (!col) return null
38
+ if (y < this.minY || y >= this.maxY) return 0
39
+ this.pos.x = x & 15; this.pos.y = y; this.pos.z = z & 15
40
+ return col.getBlockStateId(this.pos)
41
+ }
42
+
43
+ info (stateId) {
44
+ let i = this.cache.get(stateId)
45
+ if (i) return i
46
+ i = this._build(stateId)
47
+ this.cache.set(stateId, i)
48
+ return i
49
+ }
50
+
51
+ blockAt (x, y, z) {
52
+ const s = this.stateAt(x, y, z)
53
+ return this.info(s ?? 0)
54
+ }
55
+
56
+ _build (stateId) {
57
+ let b
58
+ try { b = this.Block.fromStateId(stateId, 0) } catch (e) { b = null }
59
+ const name = b?.name ?? 'air'
60
+ const props = (b && b.getProperties) ? b.getProperties() : {}
61
+ const shapes = (b && Array.isArray(b.shapes)) ? b.shapes.filter(s => s && s.length === 6) : []
62
+ const info = {
63
+ stateId,
64
+ name,
65
+ props,
66
+ shapes,
67
+ isAir: name === 'air' || name === 'cave_air' || name === 'void_air',
68
+ friction: f32(FRICTION[name] ?? 0.6),
69
+ speedFactor: f32(SPEED_FACTOR[name] ?? 1.0),
70
+ jumpFactor: f32(JUMP_FACTOR[name] ?? 1.0),
71
+ climbable: CLIMBABLE.has(name),
72
+ isTrapdoor: name.endsWith('_trapdoor'),
73
+ isLadder: name === 'ladder',
74
+ isSlime: name === 'slime_block' || name === 'slime',
75
+ isBed: name.endsWith('_bed') || name === 'bed',
76
+ isHoney: name === 'honey_block',
77
+ isCobweb: name === 'cobweb' || name === 'web',
78
+ isBerryBush: name === 'sweet_berry_bush',
79
+ isPowderSnow: name === 'powder_snow',
80
+ isScaffolding: name === 'scaffolding',
81
+ isFenceLike: name.endsWith('_fence') || name.endsWith('_wall') || name.endsWith('_fence_gate'),
82
+ fullCube: shapes.length === 1 && shapes[0].every((v, k) => v === FULL[k]),
83
+ fluid: null
84
+ }
85
+ // fluids
86
+ if (name === 'water' || name === 'lava' || name === 'flowing_water' || name === 'flowing_lava') {
87
+ const level = Number(props.level ?? 0)
88
+ const falling = level >= 8
89
+ const amount = falling ? 8 : (level === 0 ? 8 : 8 - level)
90
+ info.fluid = { type: name.includes('water') ? 'water' : 'lava', amount, falling, source: level === 0 }
91
+ } else if (props.waterlogged === true || props.waterlogged === 'true' || WATER_SOURCES.has(name)) {
92
+ info.fluid = { type: 'water', amount: 8, falling: false, source: true }
93
+ }
94
+ return info
95
+ }
96
+
97
+ fluidAt (x, y, z) {
98
+ const s = this.stateAt(x, y, z)
99
+ if (s == null) return null
100
+ return this.info(s).fluid
101
+ }
102
+
103
+ // FluidState.getOwnHeight()
104
+ static ownHeight (fluid) {
105
+ return fluid ? f32(fluid.amount / 9.0) : 0
106
+ }
107
+
108
+ // FluidState.getHeight(level, pos)
109
+ fluidHeight (x, y, z, fluid) {
110
+ if (!fluid) return 0
111
+ const above = this.fluidAt(x, y + 1, z)
112
+ if (above && above.type === fluid.type) return 1.0
113
+ return WorldView.ownHeight(fluid)
114
+ }
115
+
116
+ // Collect every collision box that could touch `bb` (BlockCollisions semantics:
117
+ // unloaded chunks collide with nothing, outside build height is air).
118
+ collisionBoxes (bb, out = []) {
119
+ const x0 = Math.floor(bb.minX - 1.0e-7) - 1; const x1 = Math.floor(bb.maxX + 1.0e-7) + 1
120
+ const y0 = Math.floor(bb.minY - 1.0e-7) - 1; const y1 = Math.floor(bb.maxY + 1.0e-7) + 1
121
+ const z0 = Math.floor(bb.minZ - 1.0e-7) - 1; const z1 = Math.floor(bb.maxZ + 1.0e-7) + 1
122
+ for (let x = x0; x <= x1; x++) {
123
+ for (let z = z0; z <= z1; z++) {
124
+ const col = this.bot.world.getColumn(x >> 4, z >> 4)
125
+ if (!col) continue
126
+ for (let y = y0; y <= y1; y++) {
127
+ if (y < this.minY || y >= this.maxY) continue
128
+ this.pos.x = x & 15; this.pos.y = y; this.pos.z = z & 15
129
+ const info = this.info(col.getBlockStateId(this.pos))
130
+ const shapes = info.shapes
131
+ for (let k = 0; k < shapes.length; k++) {
132
+ const s = shapes[k]
133
+ out.push([s[0] + x, s[1] + y, s[2] + z, s[3] + x, s[4] + y, s[5] + z])
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return out
139
+ }
140
+
141
+ noCollision (bb) {
142
+ const boxes = this.collisionBoxes(bb)
143
+ for (const b of boxes) if (bb.intersectsBox(b)) return false
144
+ return true
145
+ }
146
+
147
+ containsAnyLiquid (bb) {
148
+ const x0 = Math.floor(bb.minX); const x1 = Math.ceil(bb.maxX)
149
+ const y0 = Math.floor(bb.minY); const y1 = Math.ceil(bb.maxY)
150
+ const z0 = Math.floor(bb.minZ); const z1 = Math.ceil(bb.maxZ)
151
+ for (let x = x0; x < x1; x++) {
152
+ for (let y = y0; y < y1; y++) {
153
+ for (let z = z0; z < z1; z++) {
154
+ if (this.fluidAt(x, y, z)) return true
155
+ }
156
+ }
157
+ }
158
+ return false
159
+ }
160
+ }
161
+
162
+ module.exports = WorldView