Minecraft 21w13a代碼解析

這周的快照21w13a發(fā)布了,看看都有啥吧!
版本對(duì)照(21w11a->21w13a):https://paste.ubuntu.com/p/WWZxXbx3Tf/
說(shuō)在前面
這個(gè)版本開(kāi)始,原版代碼的閱讀和直接修改可能會(huì)更加便捷——Mojang對(duì)一些常量進(jìn)行了棄用和增加(導(dǎo)致了至少2700個(gè)類(lèi)被修改,詳見(jiàn)版本對(duì)照表),各個(gè)包下也增加了package-info,還加入了有關(guān)于混淆的注釋。下面是在SharedConstants中具體的表現(xiàn):
1.棄用的字段
@Deprecated
public static final boolean SNAPSHOT = true; // 是否為快照版本
@Deprecated
public static final int WORLD_VERSION = 2705; // 世界版本
@Deprecated
public static final String VERSION_STRING = "21w13a"; // 版本號(hào)
@Deprecated
public static final String RELEASE_TARGET = "1.17"; // 發(fā)行版本
@Deprecated
public static final int RELEASE_NETWORK_PROTOCOL_VERSION = 755; //發(fā)行網(wǎng)絡(luò)版本協(xié)議號(hào)
@Deprecated
public static final int SNAPSHOT_NETWORK_PROTOCOL_VERSION = 20; // 快照網(wǎng)絡(luò)版本協(xié)議號(hào)
@Deprecated
public static final int RESOURCE_PACK_FORMAT = 7; // 資源包版本
@Deprecated
public static final int DATA_PACK_FORMAT = 7; // 數(shù)據(jù)包版本
2.增加的字段
public static final boolean NEW_WORLD_GENERATION = true;
public static final boolean EXTENDED_WORLD_HEIGHT = true;
public static final boolean USE_NEW_RENDERSYSTEM = false; // 開(kāi)閉新渲染系統(tǒng)?
public static final boolean MULTITHREADED_RENDERING = false; // 多線(xiàn)程渲染?
public static final boolean FIX_TNT_DUPE = false; // TNT 復(fù)制修復(fù)?
public static final boolean ENABLE_SNOOPER = false;
/* 調(diào)試常量 */
public static final boolean USE_DEBUG_FEATURES = false;
public static final boolean DEBUG_HOTKEYS = false;
public static final boolean DEBUG_RENDER = false;
public static final boolean DEBUG_PATHFINDING = false;
public static final boolean DEBUG_WATER = false;
public static final boolean DEBUG_HEIGHTMAP = false;
public static final boolean DEBUG_COLLISION = false;
public static final boolean DEBUG_SHAPES = false;
public static final boolean DEBUG_NEIGHBORSUPDATE = false;
public static final boolean DEBUG_STRUCTURES = false;
public static final boolean DEBUG_LIGHT = false;
public static final boolean DEBUG_WORLDGENATTEMPT = false;
public static final boolean DEBUG_SOLID_FACE = false;
public static final boolean DEBUG_CHUNKS = false;
public static final boolean DEBUG_GAME_EVENT_LISTENERS = false;
public static final boolean DEBUG_DUMP_TEXTURE_ATLAS = false;
public static final boolean DEBUG_DUMP_INTERPOLATED_TEXTURE_FRAMES = false;
public static final boolean DEBUG_STRUCTURE_EDIT_MODE = false;
public static final boolean DEBUG_SAVE_STRUCTURES_AS_SNBT = false;
public static final boolean DEBUG_SYNCHRONOUS_GL_LOGS = false;
public static final boolean DEBUG_VERBOSE_SERVER_EVENTS = false;
public static final boolean DEBUG_NAMED_RUNNABLES = false;
public static final boolean DEBUG_GOAL_SELECTOR = false;
public static final boolean DEBUG_VILLAGE_SECTIONS = false;
public static final boolean DEBUG_BRAIN = false;
public static final boolean DEBUG_BEES = false;
public static final boolean DEBUG_RAIDS = false;
public static final boolean DEBUG_BLOCK_BREAK = false;
public static final boolean DEBUG_RESOURCE_LOAD_TIMES = false;
public static final boolean DEBUG_MONITOR_TICK_TIMES = false;
public static final boolean DEBUG_KEEP_JIGSAW_BLOCKS_DURING_STRUCTURE_GEN = false;
public static final boolean DEBUG_DONT_SAVE_WORLD = false;
public static final boolean DEBUG_LARGE_DRIPSTONE = false;
public static final boolean DEBUG_PACKET_SERIALIZATION = false;
public static final boolean DEBUG_CARVERS = false;
public static final boolean DEBUG_SMALL_SPAWN = false;
public static final boolean DEBUG_DISABLE_LIQUID_SPREADING = false;
public static final boolean DEBUG_ONLY_GENERATE_HALF_THE_WORLD = false;
public static final boolean DEBUG_DISABLE_WATER_GENERATION = false;
public static final boolean DEBUG_DISABLE_AQUIFERS = false;
public static final boolean DEBUG_DISABLE_NOISE_CAVES = false;
public static final boolean DEBUG_DISABLE_SURFACE = false;
public static final boolean DEBUG_DISABLE_CARVERS = false;
public static final boolean DEBUG_DISABLE_STRUCTURES = false;
public static final boolean DEBUG_DISABLE_FEATURES = false;
public static final boolean INGAME_DEBUG_OUTPUT = false;
public static final boolean DEBUG_SUBTITLES = false;
public static final boolean DEBUG_WORLD_RECREATE = false;
public static final boolean DEBUG_SHOW_SERVER_DEBUG_VALUES = false;
public static final boolean DEBUG_STORE_CHUNK_STACKTRACES = false;
public static final int FAKE_MS_LATENCY = 0;
public static final int FAKE_MS_JITTER = 0;
public static final Level NETTY_LEAK_DETECTION;
public static final boolean COMMAND_STACK_TRACES = false;
/* 世界設(shè)置 */
public static final int DEFAULT_MINECRAFT_PORT = 25565;
public static final float RAIN_THRESHOLD = 0.15F;
public static final long MAXIMUM_TICK_TIME_NANOS;
public static final int WORLD_RESOLUTION = 16;
public static final int MAX_CHAT_LENGTH = 256;
public static final int MAX_COMMAND_LENGTH = 32500;
public static final int TICKS_PER_SECOND = 20;
public static final int TICKS_PER_MINUTE = 1200;
public static final int TICKS_PER_GAME_DAY = 24000;
public static final float AVERAGE_GAME_TICKS_PER_RANDOM_TICK_PER_BLOCK = 1365.3334F;
public static final float AVERAGE_RANDOM_TICKS_PER_BLOCK_PER_MINUTE = 0.87890625F;
public static final float AVERAGE_RANDOM_TICKS_PER_BLOCK_PER_GAME_DAY = 17.578125F;
目前尚不清楚這些字段的用處,但是也許對(duì)理解Mojang的代碼有好處。
一.山羊
山羊是這個(gè)版本新增加的一種生物,現(xiàn)在沒(méi)有自然生成。它的主要特性是跳躍、擠奶,現(xiàn)在沖撞AI還沒(méi)有加入,應(yīng)該在下個(gè)版本就能加入。

先說(shuō)說(shuō)跳躍的特性。山羊的跳躍由兩個(gè)類(lèi)控制:LongJumpMidJump和LongJumpToRandomPos。
// 初始化跳躍AI
private static void initLongJumpActivity(Brain brain) {
brain.addActivityWithConditions(Activity.LONG_JUMP, ImmutableList.of(Pair.of(0, new LongJumpMidJump(TIMES_BETWEEN_LONG_JUMPS)), Pair.of(1, new LongJumpToRandomPos(TIMES_BETWEEN_LONG_JUMPS, 5, 5, 1.5f))), ImmutableSet.of(Pair.of(MemoryModuleType.TEMPTING_PLAYER, MemoryStatus.VALUE_ABSENT), Pair.of(MemoryModuleType.BREED_TARGET, MemoryStatus.VALUE_ABSENT), Pair.of(MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT), Pair.of(MemoryModuleType.LONG_JUMP_COOLDOWN_TICKS, MemoryStatus.VALUE_ABSENT)));
}
每次嘗試跳躍間隔是600-1200游戲刻。在跳躍之前,山羊會(huì)檢查腳下方塊,如果是蜂蜜塊,則不會(huì)跳躍。之后,山羊會(huì)在周?chē)?x9x9范圍內(nèi)尋找可以進(jìn)行跳躍的方塊,生成一個(gè)可跳躍方塊列表。在生成跳躍方塊列表后,山羊會(huì)隨機(jī)選取已經(jīng)生成的可到達(dá)位置作為要跳躍到的位置并再次檢查這個(gè)位置是否可被尋路,尋路最大距離為7格。若尋路條件判斷失敗,在下一隨機(jī)刻還會(huì)再次隨機(jī)選取判斷,一共嘗試20次。如果成功,山羊?qū)⒚鎸?duì)將要跳向的方塊,也就是"準(zhǔn)備跳躍"。"準(zhǔn)備跳躍"時(shí)長(zhǎng)40tick,在此時(shí)間內(nèi)山羊不會(huì)再次檢查方塊是否可跳躍,所以在此期間破壞將要跳向的方塊不會(huì)導(dǎo)致此AI取消。40tick結(jié)束后,山羊跳躍,跳躍最大速度為1.5m/tick,摩擦失效,跳躍提升對(duì)此無(wú)作用。落地時(shí),摩擦重新生效,速度變?yōu)橹暗?.1倍。
// 開(kāi)始AI運(yùn)作
protected void start(ServerLevel serverLevel, Mob mob, long l) {
this.chosenJump = Optional.empty();
this.findJumpTries = 20;
this.jumpCandidates.clear();
this.initialPosition = Optional.of(mob.position());
BlockPos blockPos = mob.blockPosition();
int n = blockPos.getX();
int n2 = blockPos.getY();
int n3 = blockPos.getZ();
Iterable iterable = BlockPos.betweenClosed(n - this.maxLongJumpWidth,
n2 - this.maxLongJumpHeight, n3 - this.maxLongJumpWidth,
n + this.maxLongJumpWidth, n2 + this.maxLongJumpHeight,
n3 + this.maxLongJumpWidth);
PathNavigation pathNavigation = mob.getNavigation();
for (BlockPos blockPos2 : iterable) {
double d = blockPos2.distSqr(blockPos);
if (n == blockPos2.getX() && n3 == blockPos2.getZ() || !pathNavigation.isStableDestination(blockPos2)
|| mob.getPathfindingMalus(WalkNodeEvaluator.getBlockPathTypeStatic(mob.level,blockPos2.mutable())) != 0.0f)
continue;
Optional optional = this.calculateOptimalJumpVector(mob, Vec3.atCenterOf(blockPos2));
optional.ifPresent(vec3 -> this.jumpCandidates
.add(new PossibleJump(new BlockPos(blockPos2), vec3, Mth.ceil(d))));
}
}
// AI的游戲刻運(yùn)行
protected void tick(ServerLevel serverLevel, Mob mob, long l) {
if (this.chosenJump.isPresent()) {
if (l - this.prepareJumpStart >= 40L) {
mob.yRot = mob.yBodyRot;
mob.setDiscardFriction(true);
mob.setDeltaMovement(this.chosenJump.get().getJumpVector());
mob.getBrain().setMemory(MemoryModuleType.LONG_JUMP_MID_JUMP, (Object) true);
}
} else {
--this.findJumpTries;
Optional optional = WeighedRandom.getRandomItem(serverLevel.random, this.jumpCandidates);
if (optional.isPresent()) {
this.jumpCandidates.remove(optional.get());
mob.getBrain().setMemory(MemoryModuleType.LOOK_TARGET,
(Object) new BlockPosTracker(((PossibleJump) optional.get()).getJumpTarget()));
PathNavigation pathNavigation = mob.getNavigation();
Path path = pathNavigation.createPath(((PossibleJump) optional.get()).getJumpTarget(), 0, 7);
if (path == null || !path.canReach()) {
this.chosenJump = optional;
this.prepareJumpStart = l;
}
}
}
}
那么山羊?yàn)槭裁磸倪@么高的位置摔下不會(huì)扣血呢?答案是:覆寫(xiě)了calculateFallDamage。山羊在計(jì)算下落高度時(shí)會(huì)減掉10格,所以即使跳的很高也不會(huì)摔掉血。
// 計(jì)算下落距離
protected int calculateFallDamage(float f, float f2) {
return super.calculateFallDamage(f, f2) - 10;
}
另一個(gè)特性是擠奶,這個(gè)類(lèi)似于牛。
// 當(dāng)玩家右鍵生物進(jìn)行互動(dòng)時(shí)調(diào)用此方法
public InteractionResult mobInteract(Player player, InteractionHand interactionHand) {
ItemStack itemStack = player.getItemInHand(interactionHand);
if (itemStack.is(Items.BUCKET) && !this.isBaby()) {
player.playSound(this.getMilkingSound(), 1.0f, 1.0f);
ItemStack itemStack2 = ItemUtils.createFilledResult(itemStack, player, Items.MILK_BUCKET.getDefaultInstance());
player.setItemInHand(interactionHand, itemStack2);
return InteractionResult.sidedSuccess(this.level.isClientSide);
}
return super.mobInteract(player, interactionHand);
}
二.光源方塊
光源方塊本身是基巖版的專(zhuān)屬,在此版本也加入了Java版,它是一種看不見(jiàn)的方塊,不阻礙天空光,類(lèi)似屏障只能手持同類(lèi)物品才能看到,能散發(fā)出指定強(qiáng)度的方塊光,同時(shí)也是一個(gè)含水方塊。

它的獲取和同樣身為"OP方塊"的命令方塊、結(jié)構(gòu)方塊、拼圖方塊、結(jié)構(gòu)空位一樣需要用give指令。獲取它需要輸入/give @s minecraft:light。如果要獲得指定亮度的光源方塊物品(默認(rèn)為15級(jí)亮度方塊),就要輸入/give @s minecraft:light{"BlockStateTag":{"level":<亮度0-15>}}。同樣,對(duì)一個(gè)已經(jīng)放置的光源方塊中建選取也能獲得物品(必須手持另一個(gè)光源方塊)。
// 中鍵選取方塊時(shí)調(diào)用
public ItemStack getCloneItemStack(BlockGetter blockGetter, BlockPos blockPos, BlockState blockState) {
ItemStack itemStack = super.getCloneItemStack(blockGetter, blockPos, blockState);
CompoundTag compoundTag = new CompoundTag();
compoundTag.putString(LEVEL.getName(), String.valueOf(blockState.getValue(LEVEL)));
itemStack.addTagElement("BlockStateTag", (Tag) compoundTag);
return itemStack;
}
在平常狀況下,我們無(wú)法碰到光源方塊,因?yàn)樗匠顟B(tài)下沒(méi)有包圍盒。但是當(dāng)我們手持光源方塊物品時(shí),它就能被點(diǎn)中,并且還能像屏障一樣顯示。
// 當(dāng)玩家看向這個(gè)方塊時(shí),視線(xiàn)計(jì)算碰撞使用此方法
public VoxelShape getShape(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext) {
return collisionContext.isHoldingItem(Items.LIGHT) ? Shapes.block() : Shapes.empty();
}
當(dāng)我們右鍵它時(shí),它會(huì)更改它的亮度,從0-15之間輪回。
// 當(dāng)玩家以右鍵與方塊交互時(shí),調(diào)用此方法
public InteractionResult use(BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult) {
if (!level.isClientSide) {
level.setBlock(blockPos, blockState.cycle(LEVEL), 2 /* 注:這個(gè)2(0b10)是更新標(biāo)識(shí) */);
return InteractionResult.SUCCESS;
}
return InteractionResult.CONSUME;
}
三.細(xì)雪方塊的修改
細(xì)雪方塊在這個(gè)版本進(jìn)行了一些修改,如下:
1)當(dāng)著火的生物實(shí)體進(jìn)入細(xì)雪內(nèi),火會(huì)熄滅并且這個(gè)細(xì)雪方塊消失

2)下落速度變?yōu)?.5(之前是0.99)
// 當(dāng)實(shí)體進(jìn)入方塊內(nèi)部,調(diào)用此方法
public void entityInside(BlockState blockState, Level level, BlockPos blockPos, Entity entity) {
if (!(entity instanceof LivingEntity) || ((LivingEntity) entity).getFeetBlockState().is(Blocks.POWDER_SNOW)) {
entity.makeStuckInBlock(blockState, new Vec3(0.9, 1.5, 0.9));
}
entity.setIsInPowderSnow(true);
if (entity.isOnFire()) {
level.setBlockAndUpdate(blockPos, Blocks.AIR.defaultBlockState());
level.addDestroyBlockEffect(blockPos, blockState);
}
if (level.isClientSide) {
entity.clearFire();
} else {
entity.setSharedFlagOnFire(false);
}
if (!entity.isSpectator() && (entity.xOld != entity.getX() || entity.zOld != entity.getZ()) && level.random.nextBoolean()) {
PowderSnowBlock.spawnPowderSnowParticles(level,
new Vec3(entity.getX(), blockPos.getY(), entity.getZ()));
}
}
3)實(shí)體凍傷開(kāi)始時(shí)間變?yōu)?40tick。完全冰凍后,每40tick受到1點(diǎn)傷害;對(duì)于烈焰人、巖漿怪、熾足獸這種帶有FREEZE_HURTS_EXTRA_TYPES的生物為5點(diǎn)傷害。
// aiStep中的一段代碼
boolean bl = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
if (!this.level.isClientSide) {
n = this.getTicksFrozen();
if (this.isInPowderSnow && this.canFreeze()) {
this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), n + 1));
} else {
this.setTicksFrozen(Math.max(0, n - 2));
}
}
removeFrost();
tryAddFrost();
if (!level.isClientSide && tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
n = bl ? 5 : 1;
this.hurt(DamageSource.FREEZE, n);
}
4)骷髏轉(zhuǎn)變?yōu)榱骼苏邚倪M(jìn)入細(xì)雪的140tick開(kāi)始,再經(jīng)過(guò)300tick轉(zhuǎn)變?yōu)榱骼苏摺?/p>
// Skeleton類(lèi)中的一部分代碼,執(zhí)行游戲刻
public void tick() {
if (!this.level.isClientSide && this.isAlive() && !this.isNoAi()) {
if (this.isFreezeConverting()) {
--this.conversionTime;
if (this.conversionTime < 0) {
this.doFreezeConversion();
}
} else if (this.isInPowderSnow) {
++this.inPowderSnowTime;
if (this.inPowderSnowTime >= 140) {
this.startFreezeConversion(300);
}
} else {
this.inPowderSnowTime = -1;
}
}
super.tick();
}
四.美西螈的修改
美西螈的一些bug在這個(gè)版本中被修復(fù)(即使MC-208626還在堅(jiān)持著)
1)美西螈桶現(xiàn)在能保存美西螈的信息,特有的NBT標(biāo)簽為variant(變種)和age(年齡)。可是,Bugjump忘加了美西螈CustomName的保存,所以將美西螈裝到桶里再放出來(lái)命名丟失。(各種魚(yú)桶同理)
// 用桶接
public InteractionResult mobInteract(Player player, InteractionHand interactionHand) {
return Bucketable.bucketMobPickup(player, interactionHand, this)
.orElse(super.mobInteract(player, interactionHand));
}
// 保存到桶中的額外數(shù)據(jù)
public void saveToBucketTag(ItemStack itemStack) {
Bucketable.saveDefaultDataToBucketTag(this, itemStack);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putInt(VARIANT_TAG, this.getVariant().getId());
compoundTag.putInt("Age", this.getAge());
}
// 恢復(fù)額外數(shù)據(jù)
public void loadFromBucketTag(CompoundTag compoundTag) {
Bucketable.loadDefaultDataFromBucketTag(this, compoundTag);
this.setVariant(Variant.BY_ID[compoundTag.getInt(VARIANT_TAG)]);
if (compoundTag.contains("Age")) {
this.setAge(compoundTag.getInt("Age"));
}
}
2)修復(fù)了無(wú)法清除挖掘疲勞和給予生命恢復(fù)的bug
// AxolotlAi調(diào)用了此方法
public static void applySupportingEffects(Player player) {
MobEffectInstance mobEffectInstance = player.getEffect(MobEffects.REGENERATION);
int n = 100 + (mobEffectInstance != null ? mobEffectInstance.getDuration() : 0);
player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, n, 0));
player.removeEffect(MobEffects.DIG_SLOWDOWN);
}
3)擊殺非敵對(duì)生物獲得2400tick冷卻時(shí)間

代碼源:21w13a,以Mojang Name反混淆
反混淆器:MCDynamicExchanger beta.6
[項(xiàng)目地址 https://github.com/Nickid2018/MCDynamicExchanger]
反編譯器:CFK,F(xiàn)ernFlower