From 4b6b93f0b1913a35efd45388b36b8c2800da1006 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 18 Jul 2023 12:54:27 +0100 Subject: [PATCH] Fix crash when beehive is broken by fake player (#3190) * Fix crash when beehive is broken by fake player When a beehive is broken, every nearby bee targets a random player. However, if there are no nearby players, the game crashes. This should not occur under normal (vanilla) conditions. However, if a beehive is broken by a fake player there are no players in range, and so we see a crash. * Checkstyle, my beloved * Remove public modifier * See see see --- .../event/interaction/BeehiveBlockMixin.java | 50 +++++++++++++++++++ .../fabric-events-interaction-v0.mixins.json | 1 + .../event/interaction/FakePlayerTests.java | 21 ++++++++ 3 files changed, 72 insertions(+) create mode 100644 fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/BeehiveBlockMixin.java diff --git a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/BeehiveBlockMixin.java b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/BeehiveBlockMixin.java new file mode 100644 index 000000000..e8ed4be49 --- /dev/null +++ b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/BeehiveBlockMixin.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.event.interaction; + +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.block.BeehiveBlock; +import net.minecraft.entity.passive.BeeEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +@Mixin(BeehiveBlock.class) +class BeehiveBlockMixin { + @Inject( + method = "angerNearbyBees", + cancellable = true, + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/World;getNonSpectatingEntities(Ljava/lang/Class;Lnet/minecraft/util/math/Box;)Ljava/util/List;", + ordinal = 1 // Only capture the PlayerEntity call. + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void afterNearbyBeesPlayers(World world, BlockPos pos, CallbackInfo ci, List bees, List players) { + // If a fake player broke the beehive, there will be no nearby players. This causes a crash later on as we try + // to pick a random player - we early return to avoid this. + if (players.isEmpty()) ci.cancel(); + } +} diff --git a/fabric-events-interaction-v0/src/main/resources/fabric-events-interaction-v0.mixins.json b/fabric-events-interaction-v0/src/main/resources/fabric-events-interaction-v0.mixins.json index 9156d082b..806c5161d 100644 --- a/fabric-events-interaction-v0/src/main/resources/fabric-events-interaction-v0.mixins.json +++ b/fabric-events-interaction-v0/src/main/resources/fabric-events-interaction-v0.mixins.json @@ -3,6 +3,7 @@ "package": "net.fabricmc.fabric.mixin.event.interaction", "compatibilityLevel": "JAVA_16", "mixins": [ + "BeehiveBlockMixin", "PlayerAdvancementTrackerMixin", "ServerPlayerEntityMixin", "ServerPlayerInteractionManagerMixin", diff --git a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java index 5917e8dca..2bda646cc 100644 --- a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java +++ b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java @@ -17,10 +17,12 @@ package net.fabricmc.fabric.test.event.interaction; import net.minecraft.block.Blocks; +import net.minecraft.entity.EntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUsageContext; import net.minecraft.item.Items; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.test.GameTest; import net.minecraft.test.TestContext; import net.minecraft.util.Hand; @@ -60,4 +62,23 @@ public class FakePlayerTests { context.assertTrue(signStack.isEmpty(), "Sign stack was not emptied"); context.complete(); } + + /** + * Try breaking a beehive with a fake player (see {@code BeehiveBlockMixin}). + */ + @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) + public void testFakePlayerBreakBeehive(TestContext context) { + BlockPos basePos = new BlockPos(0, 1, 0); + context.setBlockState(basePos, Blocks.BEEHIVE); + context.spawnEntity(EntityType.BEE, basePos.up()); + + ServerPlayerEntity fakePlayer = FakePlayer.get(context.getWorld()); + + BlockPos fakePlayerPos = context.getAbsolutePos(basePos.add(2, 0, 2)); + fakePlayer.setPosition(fakePlayerPos.getX(), fakePlayerPos.getY(), fakePlayerPos.getZ()); + + context.assertTrue(fakePlayer.interactionManager.tryBreakBlock(context.getAbsolutePos(basePos)), "Block was not broken"); + context.expectBlock(Blocks.AIR, basePos); + context.complete(); + } }