发现

为 Kraftland 唱片更新音乐的时候, 我注意到 Minecraft 内唱片资源包无论怎样替换都会有截断音乐的问题. 查找一些资料之后发现这似乎是 1.19.4 引入的新 特性.

经过 [MC-260346]

在 1.19.4 中, Mojang 为 Java Edition 引入了一个新功能: 和基岩版一样, 唱片机将可以与漏斗集成:

  • 在唱片播放完毕之后, 漏斗将会吸走唱片机中的唱片
  • 漏斗能将唱片导入唱片机 (来源请求)

为了实现第一条功能, Mojang 并没有选择判断歌曲长度再适时弹出唱片, 而是选择 Hardcode 一个固定的唱片时常并不允许修改. 这个 Bug 第一次出现于 23w06a 快照中.

表现

使用资源包替换 11 号唱片 (时长大于 1:11), 在播放到原版唱片的长度时立刻停止, 即使唱片机下方没有漏斗.

可能的解决方案

Dirty Trick

使用资源包添加自定义音效 (e.g. kraftland.intro)

编写数据包, 在玩家插入唱片的一刻播放自定义音效

Right Fix (Credit Apollo)

原版反编译 Code (net.minecraft.world.level.block.entity.JukeboxBlockEntity.java // Original):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class JukeboxBlockEntity extends BlockEntity implements Clearable, ContainerSingleItem {
...
@VisibleForTesting
public void startPlaying() {
this.recordStartedTick = this.tickCount;
this.isPlaying = true;
this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
this.level.levelEvent((Player) null, 1010, this.getBlockPos(), Item.getId(this.getFirstItem().getItem()));
this.setChanged();
}
...
public void setItem(int p_273461_, ItemStack p_273584_) {
if (p_273584_.is(ItemTags.MUSIC_DISCS) && this.level != null) {
this.items.set(p_273461_, p_273584_);
this.setHasRecordBlockState((Entity) null, true);
this.startPlaying();
}
}
private boolean shouldRecordStopPlaying(RecordItem p_273267_) {
return this.tickCount >= this.recordStartedTick + (long) p_273267_.getLengthInTicks() + 20 L;
}
...
}

Fix:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class JukeboxBlockEntity extends BlockEntity implements Clearable, ContainerSingleItem {
private long customDuration;
...
@VisibleForTesting
public void startPlaying(ItemStack itemStack) {
this.customDuration = itemStack.getTag() != null ? itemStack.getTag().getLong("CustomDuration") : -1;
this.recordStartedTick = this.tickCount;
this.isPlaying = true;
this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
this.level.levelEvent((Player) null, 1010, this.getBlockPos(), Item.getId(this.getFirstItem().getItem()));
this.setChanged();
}
...
public void setItem(int p_273461_, ItemStack p_273584_) {
if (p_273584_.is(ItemTags.MUSIC_DISCS) && this.level != null) {
this.items.set(p_273461_, p_273584_);
this.setHasRecordBlockState((Entity) null, true);
this.startPlaying(p_273584_);
}
}
private boolean shouldRecordStopPlaying(RecordItem p_273267_) {
return this.tickCount >= this.recordStartedTick + ((this.customDuration == -1 ? (long) p_273267_.getLengthInTicks() + 20L : customDuration * 20L));
}
...
}

在 Fix Code 中, 如果唱片存在 NBT 标签 CustomDuration, 就以此为唱片时长. 玩家使用

1
/give @s minecraft:music_disc_cat{CustomDuration:114514} 

来获得特定时长唱片