spectra-physics 1.0.0 → 1.2.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 +51 -23
- package/index.js +2 -2
- package/lib/client/clock.js +38 -0
- package/lib/client/connection.js +103 -0
- package/lib/client/controls.js +78 -0
- package/lib/client/environment.js +94 -0
- package/lib/client/index.js +81 -0
- package/lib/client/interact.js +79 -0
- package/lib/client/items.js +85 -0
- package/lib/client/protocol.js +42 -0
- package/lib/client/registries.js +86 -0
- package/lib/client/reporter.js +91 -0
- package/lib/client/riding.js +69 -0
- package/lib/client/server.js +103 -0
- package/lib/client/teleport.js +108 -0
- package/lib/client/ticker.js +129 -0
- package/lib/engine/aabb.js +5 -3
- package/lib/engine/boat.js +57 -7
- package/lib/engine/player/blocks.js +134 -0
- package/lib/engine/player/effects.js +115 -0
- package/lib/engine/player/fluids.js +150 -0
- package/lib/engine/player/index.js +220 -0
- package/lib/engine/player/input.js +89 -0
- package/lib/engine/player/move.js +141 -0
- package/lib/engine/player/pose.js +95 -0
- package/lib/engine/player/push.js +27 -0
- package/lib/engine/player/travel.js +203 -0
- package/lib/engine/player/traversal.js +290 -0
- package/lib/engine/version.js +52 -27
- package/lib/engine/world.js +85 -20
- package/package.json +2 -1
- package/lib/engine/physics.js +0 -780
- package/lib/plugin.js +0 -574
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { f32, sin, cos, degToRadF, clamp } = require('../mth')
|
|
4
|
+
const { moveRelative } = require('./input')
|
|
5
|
+
const { move } = require('./move')
|
|
6
|
+
const { fluidFallingAdjusted } = require('./fluids')
|
|
7
|
+
const blocks = require('./blocks')
|
|
8
|
+
|
|
9
|
+
const F_098 = f32(0.98)
|
|
10
|
+
const F_091 = f32(0.91)
|
|
11
|
+
const F_08 = f32(0.8)
|
|
12
|
+
const F_09 = f32(0.9)
|
|
13
|
+
const F_096 = f32(0.96)
|
|
14
|
+
const F_002 = f32(0.02)
|
|
15
|
+
const F_0026 = f32(0.025999999)
|
|
16
|
+
const F_0216 = f32(0.21600002)
|
|
17
|
+
const F_0546 = f32(0.54600006)
|
|
18
|
+
const F_015 = f32(0.15)
|
|
19
|
+
const F_042 = f32(0.42)
|
|
20
|
+
const F_01 = f32(0.1)
|
|
21
|
+
const F_02 = f32(0.2)
|
|
22
|
+
const F_004 = f32(0.04)
|
|
23
|
+
const F_06 = 0.6000000238418579
|
|
24
|
+
const F_03 = 0.30000001192092896
|
|
25
|
+
|
|
26
|
+
function flyingSpeed (player) {
|
|
27
|
+
if (!player.V.flyingSpeedLive) return player.legacyFlyingSpeed
|
|
28
|
+
if (player.abilities.flying) return player.sprinting ? f32(player.abilities.flyingSpeed * 2) : player.abilities.flyingSpeed
|
|
29
|
+
return player.sprinting ? F_0026 : F_002
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function frictionInfluencedSpeed (player, friction) {
|
|
33
|
+
if (player.onGround) return f32(player.movementSpeed() * f32(F_0216 / f32(f32(friction * friction) * friction)))
|
|
34
|
+
return flyingSpeed(player)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function handleOnClimbable (player) {
|
|
38
|
+
if (!blocks.onClimbable(player)) return
|
|
39
|
+
player.fallDistance = 0
|
|
40
|
+
player.vel.x = clamp(player.vel.x, -F_015, F_015)
|
|
41
|
+
player.vel.z = clamp(player.vel.z, -F_015, F_015)
|
|
42
|
+
let vy = Math.max(player.vel.y, -F_015)
|
|
43
|
+
if (vy < 0 && !blocks.feetBlock(player).isScaffolding && player.input.shift) vy = 0
|
|
44
|
+
player.vel.y = vy
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isFree (player, dx, dy, dz) {
|
|
48
|
+
const box = player.bb().move(dx, dy, dz)
|
|
49
|
+
return player.world.noCollision(box) && !player.world.containsAnyLiquid(box)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// LivingEntity#jumpFromGround
|
|
53
|
+
function jumpFromGround (player) {
|
|
54
|
+
const V = player.V
|
|
55
|
+
const strength = V.attributes2005 ? f32(player.env.attribute('jump_strength', 0.42)) : F_042
|
|
56
|
+
let power = f32(strength * blocks.jumpFactor(player))
|
|
57
|
+
const boost = player.env.effect('jump_boost')
|
|
58
|
+
if (boost != null) power = f32(power + f32(F_01 * (boost + 1)))
|
|
59
|
+
if (V.attributes2005 && power <= 1.0e-5) return
|
|
60
|
+
player.vel.y = V.jumpKeepsHigherY ? Math.max(power, player.vel.y) : power
|
|
61
|
+
if (!player.sprinting) return
|
|
62
|
+
const r = degToRadF(player.yRot)
|
|
63
|
+
if (V.jumpDoubleSprintBoost) {
|
|
64
|
+
player.vel.x += -sin(r) * 0.2
|
|
65
|
+
player.vel.z += cos(r) * 0.2
|
|
66
|
+
} else {
|
|
67
|
+
player.vel.x += f32(-sin(r) * F_02)
|
|
68
|
+
player.vel.z += f32(cos(r) * F_02)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// jump handling in LivingEntity#aiStep
|
|
73
|
+
function handleJump (player) {
|
|
74
|
+
if (!player.input.jumping || player.abilities.flying) {
|
|
75
|
+
player.noJumpDelay = 0
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
const inLava = player.isInLava()
|
|
79
|
+
const height = inLava ? player.fluidHeight.lava : player.fluidHeight.water
|
|
80
|
+
const inWater = player.isInWater() && height > 0
|
|
81
|
+
const threshold = player.eyeHeight() < 0.4 ? 0 : 0.4
|
|
82
|
+
if (inWater && !(player.onGround && !(height > threshold))) {
|
|
83
|
+
player.vel.y += F_004
|
|
84
|
+
} else if (inLava && !(player.onGround && !(height > threshold))) {
|
|
85
|
+
player.vel.y += F_004
|
|
86
|
+
} else if ((player.onGround || (inWater && height <= threshold)) && player.noJumpDelay === 0) {
|
|
87
|
+
jumpFromGround(player)
|
|
88
|
+
player.noJumpDelay = 10
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function travelInWater (player, ix, iy, iz, gravity, falling) {
|
|
93
|
+
const V = player.V
|
|
94
|
+
const startY = player.pos.y
|
|
95
|
+
let friction = player.sprinting ? F_09 : F_08
|
|
96
|
+
let speed = F_002
|
|
97
|
+
let strider = V.efficiencyAttributes
|
|
98
|
+
? f32(player.env.attribute('water_movement_efficiency', 0))
|
|
99
|
+
: Math.min(3, player.env.bootsEnchant('depth_strider'))
|
|
100
|
+
if (!player.onGround) strider = f32(strider * 0.5)
|
|
101
|
+
if (strider > 0) {
|
|
102
|
+
const divisor = V.efficiencyAttributes ? 1 : 3
|
|
103
|
+
friction = f32(friction + f32(f32(f32(F_0546 - friction) * strider) / divisor))
|
|
104
|
+
speed = f32(speed + f32(f32(f32(player.movementSpeed() - speed) * strider) / divisor))
|
|
105
|
+
}
|
|
106
|
+
if (player.env.effect('dolphins_grace') != null) friction = F_096
|
|
107
|
+
moveRelative(player, speed, ix, iy, iz)
|
|
108
|
+
move(player, player.vel.x, player.vel.y, player.vel.z)
|
|
109
|
+
if (player.horizontalCollision && blocks.onClimbable(player)) player.vel.y = 0.2
|
|
110
|
+
player.vel.x *= friction
|
|
111
|
+
player.vel.y *= F_08
|
|
112
|
+
player.vel.z *= friction
|
|
113
|
+
fluidFallingAdjusted(player, gravity, falling)
|
|
114
|
+
if (player.horizontalCollision && isFree(player, player.vel.x, player.vel.y + F_06 - player.pos.y + startY, player.vel.z)) {
|
|
115
|
+
player.vel.y = F_03
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function travelInLava (player, ix, iy, iz, gravity, falling) {
|
|
120
|
+
const startY = player.pos.y
|
|
121
|
+
moveRelative(player, F_002, ix, iy, iz)
|
|
122
|
+
move(player, player.vel.x, player.vel.y, player.vel.z)
|
|
123
|
+
if (player.fluidHeight.lava <= 0.4) {
|
|
124
|
+
player.vel.x *= 0.5
|
|
125
|
+
player.vel.y *= F_08
|
|
126
|
+
player.vel.z *= 0.5
|
|
127
|
+
fluidFallingAdjusted(player, gravity, falling)
|
|
128
|
+
} else {
|
|
129
|
+
player.vel.x *= 0.5
|
|
130
|
+
player.vel.y *= 0.5
|
|
131
|
+
player.vel.z *= 0.5
|
|
132
|
+
}
|
|
133
|
+
player.vel.y += -gravity / 4
|
|
134
|
+
if (player.horizontalCollision && isFree(player, player.vel.x, player.vel.y + F_06 - player.pos.y + startY, player.vel.z)) {
|
|
135
|
+
player.vel.y = F_03
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function travelInAir (player, ix, iy, iz, gravity) {
|
|
140
|
+
const below = blocks.belowPos(player)
|
|
141
|
+
const friction = player.world.blockAt(below.x, below.y, below.z).friction
|
|
142
|
+
const drag = player.onGround ? f32(friction * F_091) : F_091
|
|
143
|
+
moveRelative(player, frictionInfluencedSpeed(player, friction), ix, iy, iz)
|
|
144
|
+
handleOnClimbable(player)
|
|
145
|
+
move(player, player.vel.x, player.vel.y, player.vel.z)
|
|
146
|
+
if ((player.horizontalCollision || player.input.jumping) && (blocks.onClimbable(player) || blocks.onPowderSnowWithBoots(player))) {
|
|
147
|
+
player.vel.y = 0.2
|
|
148
|
+
}
|
|
149
|
+
let vy = player.vel.y
|
|
150
|
+
const levitation = player.env.effect('levitation')
|
|
151
|
+
if (levitation != null) {
|
|
152
|
+
vy += (0.05 * (levitation + 1) - player.vel.y) * 0.2
|
|
153
|
+
player.fallDistance = 0
|
|
154
|
+
} else if (!player.world.hasChunk(below.x, below.z)) {
|
|
155
|
+
vy = player.pos.y > player.world.minY ? -0.1 : 0
|
|
156
|
+
} else {
|
|
157
|
+
vy -= gravity
|
|
158
|
+
}
|
|
159
|
+
player.vel.x *= drag
|
|
160
|
+
player.vel.y = vy * F_098
|
|
161
|
+
player.vel.z *= drag
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// LivingEntity#travel
|
|
165
|
+
function livingTravel (player, ix, iy, iz) {
|
|
166
|
+
const falling = player.vel.y <= 0
|
|
167
|
+
let gravity = player.V.attributes2005 ? player.env.attribute('gravity', 0.08) : 0.08
|
|
168
|
+
if (falling && player.env.effect('slow_falling') != null) {
|
|
169
|
+
gravity = player.V.attributes2005 ? Math.min(gravity, 0.01) : 0.01
|
|
170
|
+
player.fallDistance = 0
|
|
171
|
+
}
|
|
172
|
+
const affected = !player.abilities.flying
|
|
173
|
+
if (player.isInWater() && affected) travelInWater(player, ix, iy, iz, gravity, falling)
|
|
174
|
+
else if (player.isInLava() && affected) travelInLava(player, ix, iy, iz, gravity, falling)
|
|
175
|
+
else travelInAir(player, ix, iy, iz, gravity)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function swimTowardsLook (player) {
|
|
179
|
+
const lookY = -sin(degToRadF(player.xRot))
|
|
180
|
+
const k = lookY < -0.2 ? 0.085 : 0.06
|
|
181
|
+
const above = player.world.fluidAt(Math.floor(player.pos.x), Math.floor(player.pos.y + 1 - 0.1), Math.floor(player.pos.z))
|
|
182
|
+
if (lookY <= 0 || player.input.jumping || above) player.vel.y += (lookY - player.vel.y) * k
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Player#travel
|
|
186
|
+
function travel (player) {
|
|
187
|
+
const ix = player.xxa
|
|
188
|
+
const iz = player.zza
|
|
189
|
+
if (player.swimming && !player.riding) swimTowardsLook(player)
|
|
190
|
+
if (!player.abilities.flying) {
|
|
191
|
+
livingTravel(player, ix, 0, iz)
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
const startVy = player.vel.y
|
|
195
|
+
const saved = player.legacyFlyingSpeed
|
|
196
|
+
if (!player.V.flyingSpeedLive) player.legacyFlyingSpeed = f32(player.abilities.flyingSpeed * (player.sprinting ? 2 : 1))
|
|
197
|
+
livingTravel(player, ix, 0, iz)
|
|
198
|
+
player.vel.y = startVy * 0.6
|
|
199
|
+
player.legacyFlyingSpeed = saved
|
|
200
|
+
player.fallDistance = 0
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = { travel, handleJump }
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
// Which blocks Entity#applyEffectsFromBlocks visits after travel (1.21.2+), and in what order. The order
|
|
3
|
+
// matters: effects such as bubble columns depend on the velocity left by the blocks visited before them.
|
|
4
|
+
// Each client version walks the movement differently; the four variants below follow those versions.
|
|
5
|
+
|
|
6
|
+
const { f32 } = require('../mth')
|
|
7
|
+
|
|
8
|
+
const EPS = 1.0e-7
|
|
9
|
+
const DEFLATE = f32(1.0e-5)
|
|
10
|
+
const SHORT_MOVE_SQ = f32(f32(0.99999) * f32(0.99999))
|
|
11
|
+
const TINY_MOVE_SQ = DEFLATE * DEFLATE
|
|
12
|
+
const FAR_SQ = 0.9999900000002526 * 0.9999900000002526
|
|
13
|
+
const STEP_LIMIT = 16
|
|
14
|
+
|
|
15
|
+
// insertion-ordered set of block positions, small enough that a linear scan beats hashing
|
|
16
|
+
class BlockList {
|
|
17
|
+
constructor () { this.coords = []; this.length = 0 }
|
|
18
|
+
|
|
19
|
+
clear () { this.length = 0; return this }
|
|
20
|
+
|
|
21
|
+
has (x, y, z) {
|
|
22
|
+
const c = this.coords
|
|
23
|
+
for (let i = 0; i < this.length * 3; i += 3) if (c[i] === x && c[i + 1] === y && c[i + 2] === z) return true
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// true when the position was not in the list yet
|
|
28
|
+
add (x, y, z) {
|
|
29
|
+
if (this.has(x, y, z)) return false
|
|
30
|
+
const i = this.length * 3
|
|
31
|
+
this.coords[i] = x; this.coords[i + 1] = y; this.coords[i + 2] = z
|
|
32
|
+
this.length++
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
forEach (visit) {
|
|
37
|
+
const c = this.coords
|
|
38
|
+
for (let i = 0; i < this.length * 3; i += 3) visit(c[i], c[i + 1], c[i + 2])
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const seenAlong = new BlockList()
|
|
43
|
+
const seenBetween = new BlockList()
|
|
44
|
+
const ordered = new BlockList()
|
|
45
|
+
|
|
46
|
+
const frac = (v) => v - Math.floor(v)
|
|
47
|
+
const sign = (v) => v === 0 ? 0 : (v > 0 ? 1 : -1)
|
|
48
|
+
|
|
49
|
+
// BlockPos.betweenClosed: x fastest, then y, then z
|
|
50
|
+
function forEachBetweenClosed (box, visit) {
|
|
51
|
+
const x0 = Math.floor(box.minX); const y0 = Math.floor(box.minY); const z0 = Math.floor(box.minZ)
|
|
52
|
+
const x1 = Math.floor(box.maxX); const y1 = Math.floor(box.maxY); const z1 = Math.floor(box.maxZ)
|
|
53
|
+
for (let z = z0; z <= z1; z++) {
|
|
54
|
+
for (let y = y0; y <= y1; y++) {
|
|
55
|
+
for (let x = x0; x <= x1; x++) visit(x, y, z)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Y first, then the smaller of x/z ... matching Direction.axisStepOrder
|
|
61
|
+
const axisStepOrder = (v) => Math.abs(v.x) < Math.abs(v.z) ? 'yzx' : 'yxz'
|
|
62
|
+
|
|
63
|
+
// BlockPos.betweenCornersInDirection: starts at the corner facing away from the direction,
|
|
64
|
+
// walks Y, then the smaller of x/z, then the other one (innermost)
|
|
65
|
+
function forEachBetweenCorners (ax, ay, az, bx, by, bz, dir, visit) {
|
|
66
|
+
const minX = Math.min(ax, bx); const minY = Math.min(ay, by); const minZ = Math.min(az, bz)
|
|
67
|
+
const sizeX = Math.max(ax, bx) - minX; const sizeY = Math.max(ay, by) - minY; const sizeZ = Math.max(az, bz) - minZ
|
|
68
|
+
const sx = dir.x >= 0 ? 1 : -1; const sy = dir.y >= 0 ? 1 : -1; const sz = dir.z >= 0 ? 1 : -1
|
|
69
|
+
const x0 = sx > 0 ? minX : minX + sizeX; const y0 = sy > 0 ? minY : minY + sizeY; const z0 = sz > 0 ? minZ : minZ + sizeZ
|
|
70
|
+
const zSecond = Math.abs(dir.x) < Math.abs(dir.z)
|
|
71
|
+
for (let i = 0; i <= sizeY; i++) {
|
|
72
|
+
const y = y0 + sy * i
|
|
73
|
+
if (zSecond) {
|
|
74
|
+
for (let j = 0; j <= sizeZ; j++) {
|
|
75
|
+
const z = z0 + sz * j
|
|
76
|
+
for (let k = 0; k <= sizeX; k++) if (visit(x0 + sx * k, y, z) === false) return false
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
for (let j = 0; j <= sizeX; j++) {
|
|
80
|
+
const x = x0 + sx * j
|
|
81
|
+
for (let k = 0; k <= sizeZ; k++) if (visit(x, y, z0 + sz * k) === false) return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clipPoint (best, delta, side, startSide, minA, maxA, minB, maxB, startA, deltaA, startB, deltaB) {
|
|
89
|
+
const t = (side - startSide) / delta
|
|
90
|
+
const a = startA + t * deltaA
|
|
91
|
+
const b = startB + t * deltaB
|
|
92
|
+
if (t > 0 && t < best.t && minA - EPS < a && a < maxA + EPS && minB - EPS < b && b < maxB + EPS) {
|
|
93
|
+
best.t = t
|
|
94
|
+
best.hit = true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// AABB.clip of the unit cube at (x, y, z) against the segment start -> end
|
|
99
|
+
function clip (x, y, z, start, end) {
|
|
100
|
+
const dx = end.x - start.x; const dy = end.y - start.y; const dz = end.z - start.z
|
|
101
|
+
const best = { t: 1, hit: false }
|
|
102
|
+
if (dx > EPS) clipPoint(best, dx, x, start.x, y, y + 1, z, z + 1, start.y, dy, start.z, dz)
|
|
103
|
+
else if (dx < -EPS) clipPoint(best, dx, x + 1, start.x, y, y + 1, z, z + 1, start.y, dy, start.z, dz)
|
|
104
|
+
if (dy > EPS) clipPoint(best, dy, y, start.y, z, z + 1, x, x + 1, start.z, dz, start.x, dx)
|
|
105
|
+
else if (dy < -EPS) clipPoint(best, dy, y + 1, start.y, z, z + 1, x, x + 1, start.z, dz, start.x, dx)
|
|
106
|
+
if (dz > EPS) clipPoint(best, dz, z, start.z, x, x + 1, y, y + 1, start.x, dx, start.y, dy)
|
|
107
|
+
else if (dz < -EPS) clipPoint(best, dz, z + 1, start.z, x, x + 1, y, y + 1, start.x, dx, start.y, dy)
|
|
108
|
+
return best.hit ? { x: start.x + best.t * dx, y: start.y + best.t * dy, z: start.z + best.t * dz } : null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// voxel walk of one corner of the box from start to end; calls hit(cx, cy, cz, point, step) on every cell it enters
|
|
112
|
+
function walk (start, end, limited, hit) {
|
|
113
|
+
const d = { x: end.x - start.x, y: end.y - start.y, z: end.z - start.z }
|
|
114
|
+
let cx = Math.floor(start.x); let cy = Math.floor(start.y); let cz = Math.floor(start.z)
|
|
115
|
+
const sx = sign(d.x); const sy = sign(d.y); const sz = sign(d.z)
|
|
116
|
+
const tx = sx === 0 ? Number.MAX_VALUE : sx / d.x
|
|
117
|
+
const ty = sy === 0 ? Number.MAX_VALUE : sy / d.y
|
|
118
|
+
const tz = sz === 0 ? Number.MAX_VALUE : sz / d.z
|
|
119
|
+
let nx = tx * (sx > 0 ? 1 - frac(start.x) : frac(start.x))
|
|
120
|
+
let ny = ty * (sy > 0 ? 1 - frac(start.y) : frac(start.y))
|
|
121
|
+
let nz = tz * (sz > 0 ? 1 - frac(start.z) : frac(start.z))
|
|
122
|
+
let steps = 0
|
|
123
|
+
while (nx <= 1 || ny <= 1 || nz <= 1) {
|
|
124
|
+
if (nx < ny) {
|
|
125
|
+
if (nx < nz) { cx += sx; nx += tx } else { cz += sz; nz += tz }
|
|
126
|
+
} else if (ny < nz) { cy += sy; ny += ty } else { cz += sz; nz += tz }
|
|
127
|
+
if (limited && steps++ > STEP_LIMIT) break
|
|
128
|
+
const point = clip(cx, cy, cz, start, end)
|
|
129
|
+
if (!point) continue
|
|
130
|
+
if (!limited) steps++
|
|
131
|
+
if (hit(cx, cy, cz, point, steps) === false) return -1
|
|
132
|
+
}
|
|
133
|
+
return steps
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const clamp = (v, lo, hi) => v < lo ? lo : (v > hi ? hi : v)
|
|
137
|
+
const clampInto = (point, cx, cy, cz) => ({
|
|
138
|
+
x: clamp(point.x, cx + DEFLATE, cx + 1 - DEFLATE),
|
|
139
|
+
y: clamp(point.y, cy + DEFLATE, cy + 1 - DEFLATE),
|
|
140
|
+
z: clamp(point.z, cz + DEFLATE, cz + 1 - DEFLATE)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// 1.21.2 - 1.21.8: the min corner of the box walks the movement; every cell hit adds a box-sized block range
|
|
144
|
+
function legacyAlongTravel (box, direction, nudge, visit) {
|
|
145
|
+
const len = Math.sqrt(direction.x ** 2 + direction.y ** 2 + direction.z ** 2)
|
|
146
|
+
const n = nudge ? { x: direction.x / len * EPS, y: direction.y / len * EPS, z: direction.z / len * EPS } : { x: 0, y: 0, z: 0 }
|
|
147
|
+
const end = { x: box.minX + n.x, y: box.minY + n.y, z: box.minZ + n.z }
|
|
148
|
+
const start = { x: box.minX - direction.x - n.x, y: box.minY - direction.y - n.y, z: box.minZ - direction.z - n.z }
|
|
149
|
+
const sizeX = box.maxX - box.minX; const sizeY = box.maxY - box.minY; const sizeZ = box.maxZ - box.minZ
|
|
150
|
+
return walk(start, end, true, (cx, cy, cz, point, step) => {
|
|
151
|
+
const c = clampInto(point, cx, cy, cz)
|
|
152
|
+
const ex = Math.floor(c.x + sizeX); const ey = Math.floor(c.y + sizeY); const ez = Math.floor(c.z + sizeZ)
|
|
153
|
+
for (let x = cx; x <= ex; x++) {
|
|
154
|
+
for (let y = cy; y <= ey; y++) {
|
|
155
|
+
for (let z = cz; z <= ez; z++) visit(x, y, z, step)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// BlockCollisions#getFurthestCorner (1.21.10+)
|
|
162
|
+
function furthestCorner (v) {
|
|
163
|
+
const ax = Math.abs(v.x); const ay = Math.abs(v.y); const az = Math.abs(v.z)
|
|
164
|
+
const xs = v.x >= 0 ? 1 : -1; const ys = v.y >= 0 ? 1 : -1; const zs = v.z >= 0 ? 1 : -1
|
|
165
|
+
if (ax <= ay && ax <= az) return { x: -xs, y: -zs, z: ys }
|
|
166
|
+
return ay <= az ? { x: zs, y: -ys, z: -xs } : { x: -ys, y: xs, z: -zs }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 1.21.9+: BlockGetter#forEachBlockIntersectedBetween
|
|
170
|
+
function modernBetween (from, to, box, visit) {
|
|
171
|
+
const d = { x: to.x - from.x, y: to.y - from.y, z: to.z - from.z }
|
|
172
|
+
if (d.x * d.x + d.y * d.y + d.z * d.z < TINY_MOVE_SQ) {
|
|
173
|
+
forEachBetweenClosed(box, (x, y, z) => visit(x, y, z, 0))
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const seen = seenBetween.clear()
|
|
177
|
+
const back = box.move(-d.x, -d.y, -d.z)
|
|
178
|
+
const ok = forEachBetweenCorners(Math.floor(back.minX), Math.floor(back.minY), Math.floor(back.minZ),
|
|
179
|
+
Math.floor(back.maxX), Math.floor(back.maxY), Math.floor(back.maxZ), d, (x, y, z) => {
|
|
180
|
+
if (visit(x, y, z, 0) === false) return false
|
|
181
|
+
seen.add(x, y, z)
|
|
182
|
+
})
|
|
183
|
+
if (!ok) return
|
|
184
|
+
const sizeX = box.maxX - box.minX; const sizeY = box.maxY - box.minY; const sizeZ = box.maxZ - box.minZ
|
|
185
|
+
const corner = furthestCorner(d)
|
|
186
|
+
const center = { x: box.minX + 0.5 * sizeX, y: box.minY + 0.5 * sizeY, z: box.minZ + 0.5 * sizeZ }
|
|
187
|
+
const end = { x: center.x + sizeX * 0.5 * corner.x, y: center.y + sizeY * 0.5 * corner.y, z: center.z + sizeZ * 0.5 * corner.z }
|
|
188
|
+
const start = { x: end.x - d.x, y: end.y - d.y, z: end.z - d.z }
|
|
189
|
+
const steps = walk(start, end, false, (cx, cy, cz, point, step) => {
|
|
190
|
+
const c = clampInto(point, cx, cy, cz)
|
|
191
|
+
const ex = Math.floor(c.x - sizeX * corner.x); const ey = Math.floor(c.y - sizeY * corner.y); const ez = Math.floor(c.z - sizeZ * corner.z)
|
|
192
|
+
return forEachBetweenCorners(cx, cy, cz, ex, ey, ez, d, (x, y, z) => {
|
|
193
|
+
if (seen.add(x, y, z)) return visit(x, y, z, step)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
if (steps < 0) return
|
|
197
|
+
forEachBetweenCorners(Math.floor(box.minX), Math.floor(box.minY), Math.floor(box.minZ),
|
|
198
|
+
Math.floor(box.maxX), Math.floor(box.maxY), Math.floor(box.maxZ), d, (x, y, z) => {
|
|
199
|
+
if (seen.add(x, y, z)) return visit(x, y, z, steps + 1)
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Visits the blocks touched by this tick's movements in game order.
|
|
205
|
+
* moves: [{ from, to, input }] where input is the movement asked of Entity#move (1.21.5+), or null.
|
|
206
|
+
* visit(x, y, z, applyVelocity)
|
|
207
|
+
*/
|
|
208
|
+
function traverseMoves (player, moves, visit) {
|
|
209
|
+
const pv = player.V.protocol
|
|
210
|
+
const boxAt = (p) => player.bbAt(p.x, p.y, p.z).deflate(DEFLATE)
|
|
211
|
+
|
|
212
|
+
if (pv < 769) { // 1.21.2 - 1.21.3: the final box, nudged walk
|
|
213
|
+
const box = player.bb().deflate(DEFLATE)
|
|
214
|
+
for (const { from, to } of moves) {
|
|
215
|
+
const d = { x: to.x - from.x, y: to.y - from.y, z: to.z - from.z }
|
|
216
|
+
if (d.x * d.x + d.y * d.y + d.z * d.z < SHORT_MOVE_SQ) {
|
|
217
|
+
forEachBetweenClosed(box, (x, y, z) => visit(x, y, z, true))
|
|
218
|
+
continue
|
|
219
|
+
}
|
|
220
|
+
const list = ordered.clear()
|
|
221
|
+
const add = (x, y, z) => { list.add(x, y, z) }
|
|
222
|
+
legacyAlongTravel(box, d, true, add)
|
|
223
|
+
forEachBetweenClosed(box, add)
|
|
224
|
+
list.forEach((x, y, z) => visit(x, y, z, true))
|
|
225
|
+
}
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (pv < 770) { // 1.21.4: box at the end of each movement
|
|
230
|
+
for (const { from, to } of moves) {
|
|
231
|
+
const box = boxAt(to)
|
|
232
|
+
const d = { x: to.x - from.x, y: to.y - from.y, z: to.z - from.z }
|
|
233
|
+
if (d.x * d.x + d.y * d.y + d.z * d.z < SHORT_MOVE_SQ) {
|
|
234
|
+
forEachBetweenClosed(box, (x, y, z) => visit(x, y, z, true))
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
const list = ordered.clear()
|
|
238
|
+
const add = (x, y, z) => { list.add(x, y, z) }
|
|
239
|
+
legacyAlongTravel(box, d, false, add)
|
|
240
|
+
forEachBetweenClosed(box, add)
|
|
241
|
+
list.forEach((x, y, z) => visit(x, y, z, true))
|
|
242
|
+
}
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const modern = pv >= 773
|
|
247
|
+
for (const move of moves) {
|
|
248
|
+
const delta = { x: move.to.x - move.from.x, y: move.to.y - move.from.y, z: move.to.z - move.from.z }
|
|
249
|
+
let budget = STEP_LIMIT
|
|
250
|
+
const segment = (from, to, limit) => {
|
|
251
|
+
const box = boxAt(to)
|
|
252
|
+
if (!modern) {
|
|
253
|
+
const d = { x: to.x - from.x, y: to.y - from.y, z: to.z - from.z }
|
|
254
|
+
if (d.x * d.x + d.y * d.y + d.z * d.z < SHORT_MOVE_SQ) {
|
|
255
|
+
forEachBetweenClosed(box, (x, y, z) => visit(x, y, z, true))
|
|
256
|
+
return 0
|
|
257
|
+
}
|
|
258
|
+
const seen = seenAlong.clear()
|
|
259
|
+
const steps = legacyAlongTravel(box, d, false, (x, y, z) => {
|
|
260
|
+
if (seen.add(x, y, z)) visit(x, y, z, true)
|
|
261
|
+
})
|
|
262
|
+
forEachBetweenClosed(box, (x, y, z) => { if (!seen.has(x, y, z)) visit(x, y, z, true) })
|
|
263
|
+
return steps
|
|
264
|
+
}
|
|
265
|
+
const far = (to.x - from.x) ** 2 + (to.y - from.y) ** 2 + (to.z - from.z) ** 2 > FAR_SQ
|
|
266
|
+
let reached = 0
|
|
267
|
+
modernBetween(from, to, box, (x, y, z, step) => {
|
|
268
|
+
if (step >= limit) return false
|
|
269
|
+
reached = step
|
|
270
|
+
visit(x, y, z, far || box.intersects(x, y, z, x + 1, y + 1, z + 1))
|
|
271
|
+
})
|
|
272
|
+
return reached + 1
|
|
273
|
+
}
|
|
274
|
+
if (move.input && (delta.x || delta.y || delta.z)) {
|
|
275
|
+
let from = move.from
|
|
276
|
+
for (const axis of axisStepOrder(modern ? move.input : delta)) {
|
|
277
|
+
const value = delta[axis]
|
|
278
|
+
if (value === 0) continue
|
|
279
|
+
const to = { ...from, [axis]: from[axis] + value }
|
|
280
|
+
budget -= segment(from, to, budget)
|
|
281
|
+
from = to
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
budget -= segment(move.from, move.to, STEP_LIMIT)
|
|
285
|
+
}
|
|
286
|
+
if (modern && budget <= 0) segment(move.to, move.to, 1)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = { traverseMoves, BlockList, forEachBetweenCorners, furthestCorner, clip }
|
package/lib/engine/version.js
CHANGED
|
@@ -1,52 +1,77 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Client behaviour that changed between 1.16.5 and 26.x, keyed by protocol number
|
|
3
|
+
// ("1.21.3" is 768, the same as 1.21.2).
|
|
4
4
|
|
|
5
5
|
const PV = {
|
|
6
6
|
'1.16.5': 754,
|
|
7
7
|
'1.17': 755,
|
|
8
|
+
'1.18.2': 758,
|
|
8
9
|
'1.19.4': 762,
|
|
9
10
|
'1.20': 763,
|
|
10
11
|
'1.20.2': 764,
|
|
11
|
-
'1.20.3': 765,
|
|
12
12
|
'1.20.5': 766,
|
|
13
13
|
'1.21': 767,
|
|
14
14
|
'1.21.2': 768,
|
|
15
15
|
'1.21.4': 769,
|
|
16
16
|
'1.21.5': 770,
|
|
17
17
|
'1.21.6': 771,
|
|
18
|
-
'1.21.9': 773
|
|
18
|
+
'1.21.9': 773,
|
|
19
|
+
'26.1': 775
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
function versionFlags (registry) {
|
|
22
|
-
const pv = registry.version
|
|
23
|
+
const pv = registry.version.version
|
|
23
24
|
const at = (name) => pv >= PV[name]
|
|
24
25
|
return {
|
|
25
26
|
protocol: pv,
|
|
26
27
|
name: registry.version.minecraftVersion,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
movementFlags: at('1.21.2'),
|
|
31
|
-
tickEnd: at('1.21.2'),
|
|
32
|
-
playerInputPacket: at('1.21.2'),
|
|
33
|
-
shiftInInput: at('1.21.6'),
|
|
34
|
-
clientLoaded: at('1.21.4'),
|
|
35
|
-
posRotBeforeConfirm: pv === PV['1.21.2'],
|
|
36
|
-
relativeTeleportVelocity: at('1.21.2'),
|
|
37
|
-
lpVelocity: at('1.21.9'),
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
|
|
29
|
+
// connection
|
|
30
|
+
configPhase: at('1.20.2'),
|
|
31
|
+
movementFlags: at('1.21.2'),
|
|
32
|
+
tickEnd: at('1.21.2'),
|
|
33
|
+
playerInputPacket: at('1.21.2'),
|
|
34
|
+
shiftInInput: at('1.21.6'),
|
|
35
|
+
clientLoaded: at('1.21.4'),
|
|
36
|
+
posRotBeforeConfirm: pv === PV['1.21.2'],
|
|
37
|
+
relativeTeleportVelocity: at('1.21.2'),
|
|
38
|
+
lpVelocity: at('1.21.9'),
|
|
39
|
+
pointThree: !at('1.18.2'), // position is only reported after moving 0.03 (later 0.0002)
|
|
40
|
+
|
|
41
|
+
// input
|
|
42
|
+
inputModel: at('1.21.5') ? 'modern' : 'legacy',
|
|
43
|
+
floatSqrt: !at('1.17'),
|
|
44
|
+
normalizeEpsilon: at('1.21.2') ? Math.fround(1.0e-5) : 1.0e-4,
|
|
45
|
+
horizontalThreshold: at('1.21.5'), // tiny horizontal speed is cut by length, not per axis
|
|
46
|
+
|
|
47
|
+
// movement
|
|
48
|
+
posFromBoundingBox: !at('1.17'),
|
|
49
|
+
epsilonCollisionFlags: at('1.18.2'),
|
|
50
|
+
keepsXOnCornerCollision: !at('1.18.2'), // 1.14 - 1.18.1 bug
|
|
51
|
+
modernMoveThreshold: at('1.21.2'),
|
|
52
|
+
modernStepUp: at('1.21'),
|
|
53
|
+
supportingBlock: at('1.20'),
|
|
54
|
+
minorCollision: at('1.19.4'),
|
|
49
55
|
fallDistanceDouble: at('1.21.5'),
|
|
56
|
+
|
|
57
|
+
// speed
|
|
58
|
+
flyingSpeedLive: at('1.19.4'),
|
|
59
|
+
airSprintFloatSum: at('1.18.2'),
|
|
60
|
+
attributes2005: at('1.20.5'),
|
|
61
|
+
efficiencyAttributes: at('1.21'), // movement_efficiency / water_movement_efficiency
|
|
62
|
+
soulSpeedBoots: pv >= 751 && !at('1.21'),
|
|
63
|
+
jumpDoubleSprintBoost: at('1.20.5'),
|
|
64
|
+
jumpKeepsHigherY: at('1.21.2'),
|
|
65
|
+
|
|
66
|
+
// world interaction
|
|
67
|
+
blockEffectsAfterTravel: at('1.21.2'),
|
|
68
|
+
blockEffectsPerAxis: at('1.21.5'),
|
|
69
|
+
insideBlockDeflate: at('1.19.4') ? 1.0e-5 : 0.001,
|
|
70
|
+
fluidRecheckInMove: pv !== PV['1.21.4'],
|
|
71
|
+
fluidInteraction: at('26.1'), // one pass over water and lava, also used by the recheck in move
|
|
72
|
+
swimmingNeedsFeetInWater: at('1.17'),
|
|
73
|
+
powderSnow: at('1.17'),
|
|
74
|
+
|
|
50
75
|
// vehicles
|
|
51
76
|
moveVehicleOnGround: at('1.21.2')
|
|
52
77
|
}
|