@@ -151,6 +151,62 @@ Wiki以前上说落沙在很高的高空落下时可能会在触及地面前就
151151
152152现存的末地门刷沙机的原理是在同一游戏刻内第3步移动的过程中使实体同时做到与末地门相交且在合适的地方着地,然后虽然末地门传送走了落沙实体(在新维度创建了新实体并把旧实体标记为移除),但落沙实体这一游戏刻的运算并不会因此终止,甚至还可以正常地复原,所以就复制了两份沙子------复原的一份和被传送的一份。不过,因为落沙实体无法通过下界传送门被传送,所以地狱门刷沙机并不可行。
153153
154+ ## [ !ADVANCED] 落沙与物品源码走读
155+
156+ ### 下落的方块:GMD 顺序
157+
158+ ``` java
159+ // FallingBlockEntity.java (1.20.1 Yarn)
160+ public void tick() {
161+ if (this . block. isAir()) { this . discard(); return ; }
162+
163+ this . timeFalling++ ;
164+ // 1. 重力:-0.04(与 TNT 相同)
165+ if (! this . hasNoGravity()) {
166+ this . setVelocity(this . getVelocity(). add(0.0 , - 0.04 , 0.0 ));
167+ }
168+ // 2. 移动
169+ this . move(MovementType . SELF , this . getVelocity());
170+
171+ if (! this . getWorld(). isClient) {
172+ // 3. 混凝土粉末高速下落时 raycast 水源
173+ boolean isConcrete = this . block. getBlock() instanceof ConcretePowderBlock ;
174+ boolean touchesWater = isConcrete && this . getWorld(). getFluidState(blockPos). isIn(FluidTags . WATER );
175+ if (isConcrete && this . getVelocity(). lengthSquared() > 1.0 ) {
176+ // 在移动路径上 raycast 检测水方块
177+ BlockHitResult hit = this . getWorld(). raycast(... );
178+ if (hit is water) { blockPos = hit. getBlockPos(); touchesWater = true ; }
179+ }
180+
181+ // 4. 着地或触水 → 复原或掉落
182+ if (this . isOnGround() || touchesWater) {
183+ this . setVelocity(this . getVelocity(). multiply(0.7 , - 0.5 , 0.7 ));
184+ if (! blockState. isOf(Blocks . MOVING_PISTON )) {
185+ if (canPlace) {
186+ this . getWorld(). setBlockState(blockPos, this . block, ... ); // 复原为方块
187+ this . discard();
188+ } else {
189+ this . discard();
190+ this . dropItem(block); // 掉落为物品
191+ }
192+ }
193+ }
194+ // 5. 超时掉落:落沙超过 100gt 且出界,或超过 600gt
195+ else if (timeFalling > 100 && outOfBounds || timeFalling > 600 ) {
196+ this . dropItem(block);
197+ this . discard();
198+ }
199+ }
200+
201+ // 6. 空气阻力:各轴 *= 0.98 — 原文提到的第 9 步
202+ this . setVelocity(this . getVelocity(). multiply(0.98 ));
203+ }
204+ ```
205+
206+ ** GMD 验证** :重力(步骤 1)→ 移动(步骤 2)→ 复原/掉落逻辑(步骤 3-5)→ 阻力(步骤 6)。与 TNT 的 GMDF 相比,落沙多了第 3-5 步的着地处理块,且步骤 6 的阻力在着地处理之后——而这正是原文说的"第 7 步是多余的"的原因:着地后实体立即被 discard,multiply(0.7, -0.5, 0.7) 和 multiply(0.98) 都对已死的实体无影响。
207+
208+ ** 第 1 步的"是否被替换"检查** :原文推测此检查用于修复 0t 活塞刷沙。1.20.1 中确实保留了 ` if (this.block.isAir()) { discard(); } ` 。如果落沙方块在计划刻创建后、实体运算前被活塞推走(方块变为空气),此检查直接废弃该实体。
209+
154210## 物品
155211
156212获取物品实体的途径有很多,常见的有:
@@ -219,6 +275,87 @@ Wiki以前上说落沙在很高的高空落下时可能会在触及地面前就
219275
220276可能得问了,物品与流体到底啥关系,在这里差不多光说流体的影响了。实际上,物品的运动研究主要还是在流体和冰道运输以及物品收集,但冰道运输只是滑度和粘液块弹射的结合,没有特殊内容,而收集装置设计上还是靠实验比较好,真正的计算并无一般规律,在此从略。
221277
278+ ## [ !ADVANCED] 物品实体源码走读
279+
280+ ### 物品的运动循环
281+
282+ ``` java
283+ // ItemEntity.java (1.20.1 Yarn)
284+ public void tick() {
285+ if (this . getStack(). isEmpty()) { this . discard(); return ; }
286+ super . tick(); // 调用 Entity.tick() → baseTick()
287+
288+ // 捡起延迟递减
289+ if (this . pickupDelay > 0 && this . pickupDelay != 32767 ) { this . pickupDelay-- ; }
290+
291+ this . prevX = this . getX(); ...
292+ Vec3d vec3d = this . getVelocity(); // 保存运动前速度
293+ float f = this . getStandingEyeHeight() - 0.11111111F ;
294+
295+ // ===== 流体分支 =====
296+ if (this . isTouchingWater() && this . getFluidHeight(FluidTags . WATER ) > f) {
297+ this . applyWaterBuoyancy(); // 水中浮力
298+ } else if (this . isInLava() && this . getFluidHeight(FluidTags . LAVA ) > f) {
299+ this . applyLavaBuoyancy(); // 岩浆浮力
300+ } else if (! this . hasNoGravity()) {
301+ this . setVelocity(this . getVelocity(). add(0.0 , - 0.04 , 0.0 )); // 空气重力
302+ }
303+
304+ // ===== 移动 + 阻力 =====
305+ if (! this . isOnGround() || this . getVelocity(). horizontalLengthSquared() > 1.0E-5F
306+ || (this . age + this . getId()) % 4 == 0 ) {
307+ this . move(MovementType . SELF , this . getVelocity());
308+ float g = 0.98F ;
309+ if (this . isOnGround()) {
310+ // 着地时:阻力乘数 = 方块滑度 × 0.98
311+ g = this . getWorld(). getBlockState(this . getVelocityAffectingPos())
312+ .getBlock(). getSlipperiness() * 0.98F ;
313+ }
314+ this . setVelocity(this . getVelocity(). multiply(g, 0.98 , g));
315+ // 着地且下落时:Y *= -0.5(防反弹)
316+ if (this . isOnGround() && this . getVelocity(). y < 0.0 ) {
317+ this . setVelocity(this . getVelocity(). multiply(1.0 , - 0.5 , 1.0 ));
318+ }
319+ }
320+
321+ // ===== 物品合并 =====
322+ boolean moved = MathHelper . floor(this . prevX) != MathHelper . floor(this . getX()) || ... ;
323+ int mergeInterval = moved ? 2 : 40 ; // 移动时每 2gt 检查一次合并
324+ if (this . age % mergeInterval == 0 && ! this . getWorld(). isClient && this . canMerge()) {
325+ this . tryMerge();
326+ }
327+
328+ // 物品寿命(6000 gt = 5 分钟)
329+ if (this . itemAge != - 32768 ) { this . itemAge++ ; }
330+ // 流体状态更新(含流体加速)
331+ this . velocityDirty = this . velocityDirty | this . updateWaterState();
332+ if (! this . getWorld(). isClient && this . itemAge >= 6000 ) { this . discard(); }
333+ }
334+ ```
335+
336+ ### 物品的流体浮力
337+
338+ ``` java
339+ // ItemEntity.java
340+ private void applyWaterBuoyancy() {
341+ Vec3d v = this . getVelocity();
342+ // 水中:X/Z *= 0.99,Y += 0.0005(仅当 Y < 0.06 时)
343+ this . setVelocity(v. x * 0.99F , v. y + (v. y < 0.06F ? 5.0E-4F : 0.0F ), v. z * 0.99F );
344+ }
345+
346+ private void applyLavaBuoyancy() {
347+ Vec3d v = this . getVelocity();
348+ // 岩浆:X/Z *= 0.95,Y += 0.0005(仅当 Y < 0.06 时)
349+ this . setVelocity(v. x * 0.95F , v. y + (v. y < 0.06F ? 5.0E-4F : 0.0F ), v. z * 0.95F );
350+ }
351+ ```
352+
353+ ** 关键发现** :物品在水中同时受空气阻力和流体阻力——` updateWaterState() ` 在末尾调用流体加速,而 ` multiply(g, 0.98, g) ` 在移动之后已经应用了空气阻力。这与原文"物品同时受到空气阻力和流体阻力"一致。
354+
355+ ** 冰道运输原理** :着地时 ` g = getSlipperiness() * 0.98 ` 。蓝冰滑度 ` 0.989 ` → ` g = 0.989 * 0.98 = 0.96922 ` ——这是物品在地面上的有效阻力乘数,远小于空气中的 ` 0.98 ` (水平方向),解释了冰道上物品能滑行极远的原因。
356+
357+ ** 物品合并的条件** :移动时每 2gt 尝试一次合并(` mergeInterval = moved ? 2 : 40 ` )。` canMerge() ` 检查两个物品是否相同类型、都未满堆叠、年龄条件等。合并时取两者中较小的 ` itemAge ` ,因此新物品的寿命从合并时重新计算。
358+
222359## 经验球
223360
224361获取经验球实体的方法主要有:
0 commit comments