Minecraft 21w08a-21w11a代码简析
Nickid2018
2021年03月21日 16:23
收录于文集
共12篇

这么多次版本更新都没法快照代码解析,那么今天都发出来吧!

版本文件对比表:

21w06a->21w07a https://paste.ubuntu.com/p/GYDNmJW6N7/

21w07a->21w08b https://paste.ubuntu.com/p/cZcxZ9KN4V/

21w08b->21w10a https://paste.ubuntu.com/p/bKRD2vF8cc/

21w10a->21w11a https://paste.ubuntu.com/p/YJZQWkhvmf/

一.铜块去蜡去锈

铜块在21w11a中能够被斧子去蜡和去锈。

斧子除蜡去锈

首先是去蜡,现在斧子能把带蜡的铜块变为不带蜡的变种,在此期间消耗1耐久度(无耐久附魔)。

去锈也类似,不过只能将铜块恢复到上一锈蚀阶段,也消耗1耐久度(无耐久附魔)

代码块
clike
自动换行
复制代码
public InteractionResult useOn(UseOnContext useOnContext) {
		Level level = useOnContext.getLevel();
		BlockPos blockPos = useOnContext.getClickedPos();
		Player player2 = useOnContext.getPlayer();
		BlockState blockState = level.getBlockState(blockPos);
		Optional<BlockState> optional = this.getStripped(blockState); // 树木去皮
		Optional optional2 = WeatheringCopper.getPrevious((BlockState) blockState); // 获取上一阶段的铜块数据
		Optional<BlockState>optional3 = Optional
				.ofNullable(((BiMap) HoneycombItem.WAX_OFF_BY_BLOCK.get()).get((Object) blockState.getBlock()))
				.map(block -> block.withPropertiesOf(blockState)); // 找到涂蜡铜块的非涂蜡变种
		Optional<Object>optional4 = Optional.empty();
		if (optional.isPresent()) {
			level.playSound(player2, blockPos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0f, 1.0f);
			optional4 = optional;
		} else if (optional2.isPresent()) {
			level.playSound(player2, blockPos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0f, 1.0f);
			level.levelEvent(player2, 3005, blockPos, 0);
			optional4 = optional2;
		} else if (optional3.isPresent()) {
			level.playSound(player2, blockPos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0f, 1.0f);
			level.levelEvent(player2, 3004, blockPos, 0);
			optional4 = optional3;
		}
		if (optional4.isPresent()) {
			level.setBlock(blockPos, (BlockState) optional4.get(), 11);
			if (player2 != null) {
				useOnContext.getItemInHand().hurtAndBreak(1, (LivingEntity) player2,
						player -> player.broadcastBreakEvent(useOnContext.getHand())); // 成功使用,扣除耐久度
			}
			return InteractionResult.sidedSuccess((boolean) level.isClientSide);
		}
		return InteractionResult.PASS;
	}
复制成功

在被闪电劈中后,铜块也能被清除锈蚀,它的实现如下:

迎接闪电的力量吧!

首先选择判定点:判断闪电是不是劈到了避雷针,如果是,那么判定点为避雷针的附着方块;如果不是,则判定点就是闪电劈中的位置。

接下来,判断判定点位置上是不是未涂蜡铜块,如果是,将这一块铜块直接还原到普通未锈蚀铜块,并进行接下来的操作;如果不是,则停止判定。

代码块
clike
自动换行
复制代码
private static void clearCopperOnLightningStrike(Level level, BlockPos blockPos) {
		// 闪电劈下时判定
		BlockPos blockPos2;
		BlockState blockState;
		BlockState blockState2 = level.getBlockState(blockPos);
		if (blockState2.is(Blocks.LIGHTNING_ROD)) {
			blockPos2 = blockPos
					.relative(((Direction) blockState2.getValue((Property) LightningRodBlock.FACING)).getOpposite());
			blockState = level.getBlockState(blockPos2);
		} else {
			blockPos2 = blockPos;
			blockState = blockState2;
		}
		if (!(blockState.getBlock() instanceof WeatheringCopper)) {
			return;
		}
		level.setBlockAndUpdate(blockPos2, WeatheringCopper.getFirst((BlockState) level.getBlockState(blockPos2)));
		BlockPos.MutableBlockPos mutableBlockPos = blockPos.mutable();
		int n = level.random.nextInt(3) + 3;
		for (int i = 0; i < n; ++i) {
			int n2 = level.random.nextInt(8) + 1;
			LightningBolt.randomWalkCleaningCopper(level, blockPos2, mutableBlockPos, n2);
		}
	}
复制成功

在修改了判定点方块后,系统将执行3-5次旁边铜块的除锈。每次都会在判定游走点周围3x3x3范围内随机选取10个方块,如果选中的方块不是铜块及其变种,则跳过;如果是,则将这个铜块清理回到上一阶段,并重新设置游走点为原先的判定点,如果这10个方块都不是铜类方块,则游走停止,不再继续,这个过程重复1-8次。

不好理解?下面是一个形象化的小栗子:

在闪电之中,诞生了3-5个神奇的人类。他们脚踩着闪电劈下的方块或者劈中的避雷针下面的方块。它们诞生时拥有1-8格体力,供他们"游走&#​34;。在闪电彻底除掉方块的锈蚀之后,他们开始随机的向前走。他们每一个人每走一步都会在他们周围的3x3x3的方块范围内找10个方块(包括自己踩的),如果找到了没有蜡的铜块,他们就会跳上这个方块,除去这个铜块的一层锈(如果有的话);如果没有找到,就原地歇息一下,什么都不干。无论他们这一步有没有成功的游走,都会消耗1体力。当他们走完了所有体力,他们就会消失。

根据这个原理,我们可以知道为了让闪电清除掉一片铜锈,我们必须要保证铜块相连或相邻(3x3x3范围内有别的铜块)。如果我们只放了一个单独的铜块,但是3x3x3内没有其它铜块,那么,即使你在其他很近的位置上放置铜块,闪电也无法清除它们的锈,因为判定点无法移动到除原先判定点的位置。同样,根据这个原理,我们可以知道一次闪电最多可以清除41个方块(起始点还原回铜块,其他都只清除到上一阶段),最远也能清除8个方块。

代码块
clike
自动换行
复制代码
private static void randomWalkCleaningCopper(Level level, BlockPos blockPos,
			BlockPos.MutableBlockPos mutableBlockPos, int n) {
			// 随机行进清除
		Optional<BlockPos> optional;
		mutableBlockPos.set((Vec3i) blockPos);
		for (int i = 0; i < n && (optional = LightningBolt.randomStepCleaningCopper(level, (BlockPos) mutableBlockPos))
				.isPresent(); ++i) {
			mutableBlockPos.set((Vec3i) optional.get());
		}
	}

	private static Optional<BlockPos>randomStepCleaningCopper(Level level, BlockPos blockPos) {
		// 随机行进中的一步
		for (BlockPos blockPos2 : BlockPos.randomInCube((Random) level.random, (int) 10, (BlockPos) blockPos,
				(int) 1)) {
			BlockState blockState2 = level.getBlockState(blockPos2);
			if (!(blockState2.getBlock() instanceof WeatheringCopper))
				continue;
			WeatheringCopper.getPrevious((BlockState) blockState2)
					.ifPresent(blockState -> level.setBlockAndUpdate(blockPos2, blockState));
			level.levelEvent(3002, blockPos2, -1);
			return Optional.of(blockPos2);
		}
		return Optional.empty();
	}
复制成功

二.垂滴叶的“涨水”特性

这个版本中有不少bug(特性),这就是一个。

逆天的水

这个特性是:当你在催熟含水的垂滴叶柄的时候,水会“涨”上来。

下面是特性的原理:当催熟垂滴叶柄的时候,系统会去寻找垂滴叶柄顶端的大型垂滴叶,如果没有,则催熟不成功;如果有的话,先读取现在催熟的方块的流体状态,然后会用这个流体状态去放置新的垂滴叶柄!所以导致了水被“顶”了起来。下面就是详细的代码:

代码块
clike
自动换行
复制代码
public void performBonemeal(ServerLevel serverLevel, Random random, BlockPos blockPos, BlockState blockState) {
		Optional optional = BlockUtil.getTopConnectedBlock((BlockGetter) serverLevel, (BlockPos) blockPos,
				(Block) blockState.getBlock(), (Direction) Direction.UP, (Block) Blocks.BIG_DRIPLEAF);
		if (!optional.isPresent()) {
			return;
		}
		BlockPos blockPos2 = (BlockPos) optional.get();
		BlockPos blockPos3 = blockPos2.above();
		FluidState fluidState = serverLevel.getFluidState(blockPos3);
		Direction direction = (Direction) blockState.getValue((Property) FACING);
		BigDripleafStemBlock.place((LevelAccessor) serverLevel, blockPos2, blockState.getFluidState(), direction);
		BigDripleafBlock.place((LevelAccessor) serverLevel, (BlockPos) blockPos3, (FluidState) fluidState,
				(Direction) direction);
	}
复制成功

三.F3+L保存性能分析数据

这是一个对于模组开发者的福利(?),这个新功能的代码net.minecraft.client.profiling里面,它记录的有JVM信息、FPS信息、渲染区块数量、待上传区块数量、其他渲染信息等。和debug命令差不多,只是没有了各种实体运算方块运算的数据,主要都是客户端的渲染信息。

F3+L触发演示

(由于CFK反编译器在此处无法翻译lambda表达式,就不放出源码了)

四.网络系统的暗改(可能?)

在21w08a的时候,network包的文件都被修改了一回,就像下面一样:

(21w07a)

代码块
clike
自动换行
复制代码
public ClientboundAddEntityPacket(int n, UUID uUID, double d, double d2, double d3, float f, float f2,
			EntityType<?> entityType, int n2, Vec3 vec3) {
		this.id = n;
		this.uuid = uUID;
		this.x = d;
		this.y = d2;
		this.z = d3;
		this.xRot = Mth.floor((float) (f * 256.0f / 360.0f));
		this.yRot = Mth.floor((float) (f2 * 256.0f / 360.0f));
		this.type = entityType;
		this.data = n2;
		this.xa = (int) (Mth.clamp((double) vec3.x, (double) -3.9, (double) 3.9) * 8000.0);
		this.ya = (int) (Mth.clamp((double) vec3.y, (double) -3.9, (double) 3.9) * 8000.0);
		this.za = (int) (Mth.clamp((double) vec3.z, (double) -3.9, (double) 3.9) * 8000.0);
	}
	public void read(FriendlyByteBuf friendlyByteBuf) throws IOException {
		this.id = friendlyByteBuf.readVarInt();
		this.uuid = friendlyByteBuf.readUUID();
		this.type = (EntityType) Registry.ENTITY_TYPE.byId(friendlyByteBuf.readVarInt());
		this.x = friendlyByteBuf.readDouble();
		this.y = friendlyByteBuf.readDouble();
		this.z = friendlyByteBuf.readDouble();
		this.xRot = friendlyByteBuf.readByte();
		this.yRot = friendlyByteBuf.readByte();
		this.data = friendlyByteBuf.readInt();
		this.xa = friendlyByteBuf.readShort();
		this.ya = friendlyByteBuf.readShort();
		this.za = friendlyByteBuf.readShort();
	}
复制成功

(21w11a)

代码块
clike
自动换行
复制代码
public ClientboundAddEntityPacket(FriendlyByteBuf friendlyByteBuf) {
		this.id = friendlyByteBuf.readVarInt();
		this.uuid = friendlyByteBuf.readUUID();
		this.type = (EntityType) Registry.ENTITY_TYPE.byId(friendlyByteBuf.readVarInt());
		this.x = friendlyByteBuf.readDouble();
		this.y = friendlyByteBuf.readDouble();
		this.z = friendlyByteBuf.readDouble();
		this.xRot = friendlyByteBuf.readByte();
		this.yRot = friendlyByteBuf.readByte();
		this.data = friendlyByteBuf.readInt();
		this.xa = friendlyByteBuf.readShort();
		this.ya = friendlyByteBuf.readShort();
		this.za = friendlyByteBuf.readShort();
	}
复制成功

可以看到,在21w08a后构造函数和read方法就整合到一起了,这也许代表了MC网络系统即将优化?(也许想多了)


代码源:21w11a,以Mojang Name反混淆

反混淆器:MCDynamicExchanger(beta6)

[项目地址 https://github.com/Nickid2018/MCDynamicExchanger]

如果专栏中有问题,欢迎在评论区指出。