diff options
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayer.java.patch')
-rw-r--r-- | patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayer.java.patch | 2591 |
1 files changed, 2591 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayer.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayer.java.patch new file mode 100644 index 0000000000..349aee0517 --- /dev/null +++ b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayer.java.patch @@ -0,0 +1,2591 @@ +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -4,10 +4,14 @@ + import com.mojang.authlib.GameProfile; + import com.mojang.datafixers.util.Either; + import com.mojang.logging.LogUtils; ++import com.mojang.serialization.DataResult; + import com.mojang.serialization.Dynamic; + import java.net.InetSocketAddress; ++import java.net.SocketAddress; + import java.util.Collection; ++import java.util.Iterator; + import java.util.List; ++import java.util.Objects; + import java.util.Optional; + import java.util.OptionalInt; + import java.util.Set; +@@ -35,9 +39,9 @@ + import net.minecraft.network.chat.CommonComponents; + import net.minecraft.network.chat.Component; + import net.minecraft.network.chat.HoverEvent; ++import net.minecraft.network.chat.MutableComponent; + import net.minecraft.network.chat.OutgoingChatMessage; + import net.minecraft.network.chat.RemoteChatSession; +-import net.minecraft.network.chat.Style; + import net.minecraft.network.protocol.Packet; + import net.minecraft.network.protocol.game.ClientboundAnimatePacket; + import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +@@ -91,8 +95,8 @@ + import net.minecraft.util.RandomSource; + import net.minecraft.util.Unit; + import net.minecraft.world.Container; +-import net.minecraft.world.InteractionHand; +-import net.minecraft.world.MenuProvider; ++import net.minecraft.world.EnumHand; ++import net.minecraft.world.ITileInventory; + import net.minecraft.world.damagesource.DamageSource; + import net.minecraft.world.effect.MobEffectInstance; + import net.minecraft.world.effect.MobEffects; +@@ -115,6 +119,7 @@ + import net.minecraft.world.entity.projectile.AbstractArrow; + import net.minecraft.world.entity.vehicle.AbstractMinecart; + import net.minecraft.world.entity.vehicle.Boat; ++import net.minecraft.world.food.FoodData; + import net.minecraft.world.inventory.AbstractContainerMenu; + import net.minecraft.world.inventory.ContainerListener; + import net.minecraft.world.inventory.ContainerSynchronizer; +@@ -128,6 +133,7 @@ + import net.minecraft.world.item.ServerItemCooldowns; + import net.minecraft.world.item.WrittenBookItem; + import net.minecraft.world.item.crafting.RecipeHolder; ++import net.minecraft.world.item.enchantment.EnchantmentHelper; + import net.minecraft.world.item.trading.MerchantOffers; + import net.minecraft.world.level.ChunkPos; + import net.minecraft.world.level.GameRules; +@@ -135,26 +141,53 @@ + import net.minecraft.world.level.Level; + import net.minecraft.world.level.biome.BiomeManager; + import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.block.ChestBlock; + import net.minecraft.world.level.block.HorizontalDirectionalBlock; + import net.minecraft.world.level.block.NetherPortalBlock; + import net.minecraft.world.level.block.entity.BlockEntity; + import net.minecraft.world.level.block.entity.CommandBlockEntity; + import net.minecraft.world.level.block.entity.SignBlockEntity; +-import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.block.state.IBlockData; + import net.minecraft.world.level.border.WorldBorder; ++import net.minecraft.world.level.dimension.LevelStem; + import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.level.portal.PortalInfo; +-import net.minecraft.world.level.storage.LevelData; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.Vec3; + import net.minecraft.world.scores.PlayerTeam; + import net.minecraft.world.scores.ScoreAccess; + import net.minecraft.world.scores.ScoreHolder; ++import org.slf4j.Logger; ++import net.minecraft.world.damagesource.CombatTracker; ++import net.minecraft.world.scores.Scoreboard; + import net.minecraft.world.scores.Team; + import net.minecraft.world.scores.criteria.ObjectiveCriteria; +-import org.slf4j.Logger; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.WeatherType; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.CraftWorldBorder; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.event.CraftPortalEvent; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftDimensionUtil; ++import org.bukkit.craftbukkit.util.CraftLocation; ++import org.bukkit.event.entity.EntityExhaustionEvent; ++import org.bukkit.event.player.PlayerBedLeaveEvent; ++import org.bukkit.event.player.PlayerChangedMainHandEvent; ++import org.bukkit.event.player.PlayerChangedWorldEvent; ++import org.bukkit.event.player.PlayerLocaleChangeEvent; ++import org.bukkit.event.player.PlayerPortalEvent; ++import org.bukkit.event.player.PlayerSpawnChangeEvent; ++import org.bukkit.event.player.PlayerTeleportEvent; ++import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; ++import org.bukkit.event.player.PlayerToggleSneakEvent; ++import org.bukkit.inventory.MainHand; ++// CraftBukkit end + + public class ServerPlayer extends Player { ++ + private static final Logger LOGGER = LogUtils.getLogger(); + private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; + private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; +@@ -173,31 +206,31 @@ + private float lastSentHealth = -1.0E8F; + private int lastSentFood = -99999999; + private boolean lastFoodSaturationZero = true; +- private int lastSentExp = -99999999; +- private int spawnInvulnerableTime = 60; +- private ChatVisiblity chatVisibility = ChatVisiblity.FULL; +- private boolean canChatColor = true; +- private long lastActionTime = Util.getMillis(); ++ public int lastSentExp = -99999999; ++ public int spawnInvulnerableTime = 60; ++ private ChatVisiblity chatVisibility; ++ private boolean canChatColor; ++ private long lastActionTime; + @Nullable + private Entity camera; +- private boolean isChangingDimension; ++ public boolean isChangingDimension; + private boolean seenCredits; +- private final ServerRecipeBook recipeBook = new ServerRecipeBook(); ++ private final ServerRecipeBook recipeBook; + @Nullable + private Vec3 levitationStartPos; + private int levitationStartTime; + private boolean disconnected; +- private int requestedViewDistance = 2; +- private String language = "en_us"; ++ private int requestedViewDistance; ++ public String language = "en_us"; // CraftBukkit - default + @Nullable + private Vec3 startingToFallPosition; + @Nullable + private Vec3 enteredNetherPosition; + @Nullable + private Vec3 enteredLavaOnVehiclePosition; +- private SectionPos lastSectionPos = SectionPos.of(0, 0, 0); +- private ChunkTrackingView chunkTrackingView = ChunkTrackingView.EMPTY; +- private ResourceKey<Level> respawnDimension = Level.OVERWORLD; ++ private SectionPos lastSectionPos; ++ private ChunkTrackingView chunkTrackingView; ++ private ResourceKey<Level> respawnDimension; + @Nullable + private BlockPos respawnPosition; + private boolean respawnForced; +@@ -205,109 +238,182 @@ + private final TextFilter textFilter; + private boolean textFilteringEnabled; + private boolean allowsListing; +- private WardenSpawnTracker wardenSpawnTracker = new WardenSpawnTracker(0, 0, 0); +- private final ContainerSynchronizer containerSynchronizer = new ContainerSynchronizer() { +- @Override +- public void sendInitialData(AbstractContainerMenu container, NonNullList<ItemStack> items, ItemStack carriedItem, int[] initialData) { +- ServerPlayer.this.connection +- .send(new ClientboundContainerSetContentPacket(container.containerId, container.incrementStateId(), items, carriedItem)); ++ private WardenSpawnTracker wardenSpawnTracker; ++ private final ContainerSynchronizer containerSynchronizer; ++ private final ContainerListener containerListener; ++ @Nullable ++ private RemoteChatSession chatSession; ++ private int containerCounter; ++ public boolean wonGame; + +- for (int i = 0; i < initialData.length; i++) { +- this.broadcastDataValue(container, i, initialData[i]); ++ // CraftBukkit start ++ public String displayName; ++ public Component listName; ++ public org.bukkit.Location compassTarget; ++ public int newExp = 0; ++ public int newLevel = 0; ++ public int newTotalExp = 0; ++ public boolean keepLevel = false; ++ public double maxHealthCache; ++ public boolean joining = true; ++ public boolean sentListPacket = false; ++ public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent ++ // CraftBukkit end ++ ++ public ServerPlayer(MinecraftServer minecraftserver, ServerLevel worldserver, GameProfile gameprofile, ClientInformation clientinformation) { ++ super(worldserver, worldserver.getSharedSpawnPos(), worldserver.getSharedSpawnAngle(), gameprofile); ++ this.chatVisibility = ChatVisiblity.FULL; ++ this.canChatColor = true; ++ this.lastActionTime = Util.getMillis(); ++ this.recipeBook = new ServerRecipeBook(); ++ this.requestedViewDistance = 2; ++ this.language = "en_us"; ++ this.lastSectionPos = SectionPos.of(0, 0, 0); ++ this.chunkTrackingView = ChunkTrackingView.EMPTY; ++ this.respawnDimension = Level.OVERWORLD; ++ this.wardenSpawnTracker = new WardenSpawnTracker(0, 0, 0); ++ this.containerSynchronizer = new ContainerSynchronizer() { ++ @Override ++ public void sendInitialData(AbstractContainerMenu container, NonNullList<ItemStack> items, ItemStack carriedItem, int[] initialData) { ++ ServerPlayer.this.connection.send(new ClientboundContainerSetContentPacket(container.containerId, container.incrementStateId(), items, carriedItem)); ++ ++ for (int i = 0; i < initialData.length; ++i) { ++ this.broadcastDataValue(container, i, initialData[i]); ++ } ++ + } +- } + +- @Override +- public void sendSlotChange(AbstractContainerMenu container, int slot, ItemStack itemStack) { +- ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(container.containerId, container.incrementStateId(), slot, itemStack)); +- } ++ @Override ++ public void sendSlotChange(AbstractContainerMenu container, int slot, ItemStack itemStack) { ++ ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(container.containerId, container.incrementStateId(), slot, itemStack)); ++ } + +- @Override +- public void sendCarriedChange(AbstractContainerMenu containerMenu, ItemStack stack) { +- ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(-1, containerMenu.incrementStateId(), -1, stack)); +- } ++ @Override ++ public void sendCarriedChange(AbstractContainerMenu containerMenu, ItemStack stack) { ++ ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(-1, containerMenu.incrementStateId(), -1, stack)); ++ } + +- @Override +- public void sendDataChange(AbstractContainerMenu container, int id, int value) { +- this.broadcastDataValue(container, id, value); +- } ++ @Override ++ public void sendDataChange(AbstractContainerMenu container, int id, int value) { ++ this.broadcastDataValue(container, id, value); ++ } + +- private void broadcastDataValue(AbstractContainerMenu container, int id, int value) { +- ServerPlayer.this.connection.send(new ClientboundContainerSetDataPacket(container.containerId, id, value)); +- } +- }; +- private final ContainerListener containerListener = new ContainerListener() { +- @Override +- public void slotChanged(AbstractContainerMenu containerToSend, int dataSlotIndex, ItemStack stack) { +- Slot slot = containerToSend.getSlot(dataSlotIndex); +- if (!(slot instanceof ResultSlot)) { +- if (slot.container == ServerPlayer.this.getInventory()) { +- CriteriaTriggers.INVENTORY_CHANGED.trigger(ServerPlayer.this, ServerPlayer.this.getInventory(), stack); ++ private void broadcastDataValue(AbstractContainerMenu container, int id, int value) { ++ ServerPlayer.this.connection.send(new ClientboundContainerSetDataPacket(container.containerId, id, value)); ++ } ++ }; ++ this.containerListener = new ContainerListener() { ++ @Override ++ public void slotChanged(AbstractContainerMenu containerToSend, int dataSlotIndex, ItemStack stack) { ++ Slot slot = containerToSend.getSlot(dataSlotIndex); ++ ++ if (!(slot instanceof ResultSlot)) { ++ if (slot.container == ServerPlayer.this.getInventory()) { ++ CriteriaTriggers.INVENTORY_CHANGED.trigger(ServerPlayer.this, ServerPlayer.this.getInventory(), stack); ++ } ++ + } + } +- } + +- @Override +- public void dataChanged(AbstractContainerMenu containerMenu, int dataSlotIndex, int value) { ++ @Override ++ public void dataChanged(AbstractContainerMenu containerMenu, int dataSlotIndex, int value) {} ++ }; ++ this.textFilter = minecraftserver.createTextFilterForPlayer(this); ++ this.gameMode = minecraftserver.createGameModeForPlayer(this); ++ this.server = minecraftserver; ++ this.stats = minecraftserver.getPlayerList().getPlayerStats(this); ++ this.advancements = minecraftserver.getPlayerList().getPlayerAdvancements(this); ++ this.setMaxUpStep(1.0F); ++ this.fudgeSpawnLocation(worldserver); ++ this.updateOptions(clientinformation); ++ ++ // CraftBukkit start ++ this.displayName = this.getScoreboardName(); ++ this.bukkitPickUpLoot = true; ++ this.maxHealthCache = this.getMaxHealth(); ++ } ++ ++ // Yes, this doesn't match Vanilla, but it's the best we can do for now. ++ // If this is an issue, PRs are welcome ++ public final BlockPos getSpawnPoint(ServerLevel worldserver) { ++ BlockPos blockposition = worldserver.getSharedSpawnPos(); ++ ++ if (worldserver.dimensionType().hasSkyLight() && worldserver.serverLevelData.getGameType() != GameType.ADVENTURE) { ++ int i = Math.max(0, this.server.getSpawnRadius(worldserver)); ++ int j = Mth.floor(worldserver.getWorldBorder().getDistanceToBorder((double) blockposition.getX(), (double) blockposition.getZ())); ++ ++ if (j < i) { ++ i = j; ++ } ++ ++ if (j <= 1) { ++ i = 1; ++ } ++ ++ long k = (long) (i * 2 + 1); ++ long l = k * k; ++ int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; ++ int j1 = this.getCoprime(i1); ++ int k1 = RandomSource.create().nextInt(i1); ++ ++ for (int l1 = 0; l1 < i1; ++l1) { ++ int i2 = (k1 + j1 * l1) % i1; ++ int j2 = i2 % (i * 2 + 1); ++ int k2 = i2 / (i * 2 + 1); ++ BlockPos blockposition1 = PlayerRespawnLogic.getOverworldRespawnPos(worldserver, blockposition.getX() + j2 - i, blockposition.getZ() + k2 - i); ++ ++ if (blockposition1 != null) { ++ return blockposition1; ++ } ++ } + } +- }; +- @Nullable +- private RemoteChatSession chatSession; +- private int containerCounter; +- public boolean wonGame; + +- public ServerPlayer(MinecraftServer minecraftServer, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) { +- super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile); +- this.textFilter = minecraftServer.createTextFilterForPlayer(this); +- this.gameMode = minecraftServer.createGameModeForPlayer(this); +- this.server = minecraftServer; +- this.stats = minecraftServer.getPlayerList().getPlayerStats(this); +- this.advancements = minecraftServer.getPlayerList().getPlayerAdvancements(this); +- this.setMaxUpStep(1.0F); +- this.fudgeSpawnLocation(level); +- this.updateOptions(clientInformation); ++ return blockposition; + } ++ // CraftBukkit end + + private void fudgeSpawnLocation(ServerLevel level) { +- BlockPos sharedSpawnPos = level.getSharedSpawnPos(); +- if (level.dimensionType().hasSkyLight() && level.getServer().getWorldData().getGameType() != GameType.ADVENTURE) { +- int max = Math.max(0, this.server.getSpawnRadius(level)); +- int floor = Mth.floor(level.getWorldBorder().getDistanceToBorder((double)sharedSpawnPos.getX(), (double)sharedSpawnPos.getZ())); +- if (floor < max) { +- max = floor; ++ BlockPos blockposition = level.getSharedSpawnPos(); ++ ++ if (level.dimensionType().hasSkyLight() && level.serverLevelData.getGameType() != GameType.ADVENTURE) { // CraftBukkit ++ int i = Math.max(0, this.server.getSpawnRadius(level)); ++ int j = Mth.floor(level.getWorldBorder().getDistanceToBorder((double) blockposition.getX(), (double) blockposition.getZ())); ++ ++ if (j < i) { ++ i = j; + } + +- if (floor <= 1) { +- max = 1; ++ if (j <= 1) { ++ i = 1; + } + +- long l = (long)(max * 2 + 1); +- long l1 = l * l; +- int i = l1 > 2147483647L ? Integer.MAX_VALUE : (int)l1; +- int coprime = this.getCoprime(i); +- int randomInt = RandomSource.create().nextInt(i); ++ long k = (long) (i * 2 + 1); ++ long l = k * k; ++ int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; ++ int j1 = this.getCoprime(i1); ++ int k1 = RandomSource.create().nextInt(i1); + +- for (int i1 = 0; i1 < i; i1++) { +- int i2 = (randomInt + coprime * i1) % i; +- int i3 = i2 % (max * 2 + 1); +- int i4 = i2 / (max * 2 + 1); +- BlockPos overworldRespawnPos = PlayerRespawnLogic.getOverworldRespawnPos( +- level, sharedSpawnPos.getX() + i3 - max, sharedSpawnPos.getZ() + i4 - max +- ); +- if (overworldRespawnPos != null) { +- this.moveTo(overworldRespawnPos, 0.0F, 0.0F); +- if (level.noCollision(this)) { ++ for (int l1 = 0; l1 < i1; ++l1) { ++ int i2 = (k1 + j1 * l1) % i1; ++ int j2 = i2 % (i * 2 + 1); ++ int k2 = i2 / (i * 2 + 1); ++ BlockPos blockposition1 = PlayerRespawnLogic.getOverworldRespawnPos(level, blockposition.getX() + j2 - i, blockposition.getZ() + k2 - i); ++ ++ if (blockposition1 != null) { ++ this.moveTo(blockposition1, 0.0F, 0.0F); ++ if (level.noCollision((Entity) this)) { + break; + } + } + } + } else { +- this.moveTo(sharedSpawnPos, 0.0F, 0.0F); ++ this.moveTo(blockposition, 0.0F, 0.0F); + +- while (!level.noCollision(this) && this.getY() < (double)(level.getMaxBuildHeight() - 1)) { +- this.setPos(this.getX(), this.getY() + 1.0, this.getZ()); ++ while (!level.noCollision((Entity) this) && this.getY() < (double) (level.getMaxBuildHeight() - 1)) { ++ this.setPos(this.getX(), this.getY() + 1.0D, this.getZ()); + } + } ++ + } + + private int getCoprime(int spawnArea) { +@@ -318,67 +424,101 @@ + public void readAdditionalSaveData(CompoundTag compound) { + super.readAdditionalSaveData(compound); + if (compound.contains("warden_spawn_tracker", 10)) { +- WardenSpawnTracker.CODEC +- .parse(new Dynamic<>(NbtOps.INSTANCE, compound.get("warden_spawn_tracker"))) +- .resultOrPartial(LOGGER::error) +- .ifPresent(wardenSpawnTracker -> this.wardenSpawnTracker = wardenSpawnTracker); ++ DataResult<WardenSpawnTracker> dataresult = WardenSpawnTracker.CODEC.parse(new Dynamic(NbtOps.INSTANCE, compound.get("warden_spawn_tracker"))); // CraftBukkit - decompile error ++ Logger logger = ServerPlayer.LOGGER; ++ ++ Objects.requireNonNull(logger); ++ dataresult.resultOrPartial(logger::error).ifPresent((wardenspawntracker) -> { ++ this.wardenSpawnTracker = wardenspawntracker; ++ }); + } + + if (compound.contains("enteredNetherPosition", 10)) { +- CompoundTag compound1 = compound.getCompound("enteredNetherPosition"); +- this.enteredNetherPosition = new Vec3(compound1.getDouble("x"), compound1.getDouble("y"), compound1.getDouble("z")); ++ CompoundTag nbttagcompound1 = compound.getCompound("enteredNetherPosition"); ++ ++ this.enteredNetherPosition = new Vec3(nbttagcompound1.getDouble("x"), nbttagcompound1.getDouble("y"), nbttagcompound1.getDouble("z")); + } + + this.seenCredits = compound.getBoolean("seenCredits"); + if (compound.contains("recipeBook", 10)) { + this.recipeBook.fromNbt(compound.getCompound("recipeBook"), this.server.getRecipeManager()); + } ++ this.getBukkitEntity().readExtraData(compound); // CraftBukkit + + if (this.isSleeping()) { + this.stopSleeping(); + } + ++ // CraftBukkit start ++ String spawnWorld = compound.getString("SpawnWorld"); ++ CraftWorld oldWorld = (CraftWorld) Bukkit.getWorld(spawnWorld); ++ if (oldWorld != null) { ++ this.respawnDimension = oldWorld.getHandle().dimension(); ++ } ++ // CraftBukkit end ++ + if (compound.contains("SpawnX", 99) && compound.contains("SpawnY", 99) && compound.contains("SpawnZ", 99)) { + this.respawnPosition = new BlockPos(compound.getInt("SpawnX"), compound.getInt("SpawnY"), compound.getInt("SpawnZ")); + this.respawnForced = compound.getBoolean("SpawnForced"); + this.respawnAngle = compound.getFloat("SpawnAngle"); + if (compound.contains("SpawnDimension")) { +- this.respawnDimension = Level.RESOURCE_KEY_CODEC +- .parse(NbtOps.INSTANCE, compound.get("SpawnDimension")) +- .resultOrPartial(LOGGER::error) +- .orElse(Level.OVERWORLD); ++ DataResult<ResourceKey<Level>> dataresult1 = Level.RESOURCE_KEY_CODEC.parse(NbtOps.INSTANCE, compound.get("SpawnDimension")); // CraftBukkit - decompile error ++ Logger logger1 = ServerPlayer.LOGGER; ++ ++ Objects.requireNonNull(logger1); ++ this.respawnDimension = (ResourceKey) dataresult1.resultOrPartial(logger1::error).orElse(Level.OVERWORLD); + } + } ++ + } + + @Override + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +- WardenSpawnTracker.CODEC +- .encodeStart(NbtOps.INSTANCE, this.wardenSpawnTracker) +- .resultOrPartial(LOGGER::error) +- .ifPresent(tag -> compound.put("warden_spawn_tracker", tag)); ++ DataResult<Tag> dataresult = WardenSpawnTracker.CODEC.encodeStart(NbtOps.INSTANCE, this.wardenSpawnTracker); // CraftBukkit - decompile error ++ Logger logger = ServerPlayer.LOGGER; ++ ++ Objects.requireNonNull(logger); ++ dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { ++ compound.put("warden_spawn_tracker", nbtbase); ++ }); + this.storeGameTypes(compound); + compound.putBoolean("seenCredits", this.seenCredits); + if (this.enteredNetherPosition != null) { +- CompoundTag compoundTag = new CompoundTag(); +- compoundTag.putDouble("x", this.enteredNetherPosition.x); +- compoundTag.putDouble("y", this.enteredNetherPosition.y); +- compoundTag.putDouble("z", this.enteredNetherPosition.z); +- compound.put("enteredNetherPosition", compoundTag); ++ CompoundTag nbttagcompound1 = new CompoundTag(); ++ ++ nbttagcompound1.putDouble("x", this.enteredNetherPosition.x); ++ nbttagcompound1.putDouble("y", this.enteredNetherPosition.y); ++ nbttagcompound1.putDouble("z", this.enteredNetherPosition.z); ++ compound.put("enteredNetherPosition", nbttagcompound1); + } + +- Entity rootVehicle = this.getRootVehicle(); +- Entity vehicle = this.getVehicle(); +- if (vehicle != null && rootVehicle != this && rootVehicle.hasExactlyOnePlayerPassenger()) { +- CompoundTag compoundTag1 = new CompoundTag(); +- CompoundTag compoundTag2 = new CompoundTag(); +- rootVehicle.save(compoundTag2); +- compoundTag1.putUUID("Attach", vehicle.getUUID()); +- compoundTag1.put("Entity", compoundTag2); +- compound.put("RootVehicle", compoundTag1); ++ Entity entity = this.getRootVehicle(); ++ Entity entity1 = this.getVehicle(); ++ ++ // CraftBukkit start - handle non-persistent vehicles ++ boolean persistVehicle = true; ++ if (entity1 != null) { ++ Entity vehicle; ++ for (vehicle = entity1; vehicle != null; vehicle = vehicle.getVehicle()) { ++ if (!vehicle.persist) { ++ persistVehicle = false; ++ break; ++ } ++ } + } + ++ if (persistVehicle && entity1 != null && entity != this && entity.hasExactlyOnePlayerPassenger()) { ++ // CraftBukkit end ++ CompoundTag nbttagcompound2 = new CompoundTag(); ++ CompoundTag nbttagcompound3 = new CompoundTag(); ++ ++ entity.save(nbttagcompound3); ++ nbttagcompound2.putUUID("Attach", entity1.getUUID()); ++ nbttagcompound2.put("Entity", nbttagcompound3); ++ compound.put("RootVehicle", nbttagcompound2); ++ } ++ + compound.put("recipeBook", this.recipeBook.toNbt()); + compound.putString("Dimension", this.level().dimension().location().toString()); + if (this.respawnPosition != null) { +@@ -387,17 +527,45 @@ + compound.putInt("SpawnZ", this.respawnPosition.getZ()); + compound.putBoolean("SpawnForced", this.respawnForced); + compound.putFloat("SpawnAngle", this.respawnAngle); +- ResourceLocation.CODEC +- .encodeStart(NbtOps.INSTANCE, this.respawnDimension.location()) +- .resultOrPartial(LOGGER::error) +- .ifPresent(tag -> compound.put("SpawnDimension", tag)); ++ dataresult = ResourceLocation.CODEC.encodeStart(NbtOps.INSTANCE, this.respawnDimension.location()); ++ logger = ServerPlayer.LOGGER; ++ Objects.requireNonNull(logger); ++ dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { ++ compound.put("SpawnDimension", nbtbase); ++ }); + } ++ this.getBukkitEntity().setExtraData(compound); // CraftBukkit ++ + } + ++ // CraftBukkit start - World fallback code, either respawn location or global spawn ++ public void spawnIn(Level world) { ++ this.setLevel(world); ++ if (world == null) { ++ this.unsetRemoved(); ++ Vec3 position = null; ++ if (this.respawnDimension != null) { ++ world = this.server.getLevel(this.respawnDimension); ++ if (world != null && this.getRespawnPosition() != null) { ++ position = Player.findRespawnPositionAndUseSpawnBlock((ServerLevel) world, this.getRespawnPosition(), this.getRespawnAngle(), false, false).orElse(null); ++ } ++ } ++ if (world == null || position == null) { ++ world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); ++ position = Vec3.atCenterOf(world.getSharedSpawnPos()); ++ } ++ this.setLevel(world); ++ this.setPos(position); ++ } ++ this.gameMode.setLevel((ServerLevel) world); ++ } ++ // CraftBukkit end ++ + public void setExperiencePoints(int experiencePoints) { +- float f = (float)this.getXpNeededForNextLevel(); ++ float f = (float) this.getXpNeededForNextLevel(); + float f1 = (f - 1.0F) / f; +- this.experienceProgress = Mth.clamp((float)experiencePoints / f, 0.0F, f1); ++ ++ this.experienceProgress = Mth.clamp((float) experiencePoints / f, 0.0F, f1); + this.lastSentExp = -1; + } + +@@ -418,7 +586,7 @@ + this.lastSentExp = -1; + } + +- private void initMenu(AbstractContainerMenu menu) { ++ public void initMenu(AbstractContainerMenu menu) { + menu.addSlotListener(this.containerListener); + menu.setSynchronizer(this.containerSynchronizer); + } +@@ -440,7 +608,7 @@ + } + + @Override +- protected void onInsideBlock(BlockState state) { ++ protected void onInsideBlock(IBlockData state) { + CriteriaTriggers.ENTER_BLOCK.trigger(this, state); + } + +@@ -451,11 +619,16 @@ + + @Override + public void tick() { ++ // CraftBukkit start ++ if (this.joining) { ++ this.joining = false; ++ } ++ // CraftBukkit end + this.gameMode.tick(); + this.wardenSpawnTracker.tick(); +- this.spawnInvulnerableTime--; ++ --this.spawnInvulnerableTime; + if (this.invulnerableTime > 0) { +- this.invulnerableTime--; ++ --this.invulnerableTime; + } + + this.containerMenu.broadcastChanges(); +@@ -464,10 +637,11 @@ + this.containerMenu = this.inventoryMenu; + } + +- Entity camera = this.getCamera(); +- if (camera != this) { +- if (camera.isAlive()) { +- this.absMoveTo(camera.getX(), camera.getY(), camera.getZ(), camera.getYRot(), camera.getXRot()); ++ Entity entity = this.getCamera(); ++ ++ if (entity != this) { ++ if (entity.isAlive()) { ++ this.absMoveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot()); + this.serverLevel().getChunkSource().move(this); + if (this.wantsToStopRiding()) { + this.setCamera(this); +@@ -493,20 +667,20 @@ + super.tick(); + } + +- for (int i = 0; i < this.getInventory().getContainerSize(); i++) { +- ItemStack item = this.getInventory().getItem(i); +- if (item.getItem().isComplex()) { +- Packet<?> updatePacket = ((ComplexItem)item.getItem()).getUpdatePacket(item, this.level(), this); +- if (updatePacket != null) { +- this.connection.send(updatePacket); ++ for (int i = 0; i < this.getInventory().getContainerSize(); ++i) { ++ ItemStack itemstack = this.getInventory().getItem(i); ++ ++ if (itemstack.getItem().isComplex()) { ++ Packet<?> packet = ((ComplexItem) itemstack.getItem()).getUpdatePacket(itemstack, this.level(), this); ++ ++ if (packet != null) { ++ this.connection.send(packet); + } + } + } + +- if (this.getHealth() != this.lastSentHealth +- || this.lastSentFood != this.foodData.getFoodLevel() +- || this.foodData.getSaturationLevel() == 0.0F != this.lastFoodSaturationZero) { +- this.connection.send(new ClientboundSetHealthPacket(this.getHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); ++ if (this.getHealth() != this.lastSentHealth || this.lastSentFood != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.lastFoodSaturationZero) { ++ this.connection.send(new ClientboundSetHealthPacket(this.getBukkitEntity().getScaledHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); // CraftBukkit + this.lastSentHealth = this.getHealth(); + this.lastSentFood = this.foodData.getFoodLevel(); + this.lastFoodSaturationZero = this.foodData.getSaturationLevel() == 0.0F; +@@ -519,27 +693,33 @@ + + if (this.foodData.getFoodLevel() != this.lastRecordedFoodLevel) { + this.lastRecordedFoodLevel = this.foodData.getFoodLevel(); +- this.updateScoreForCriteria(ObjectiveCriteria.FOOD, Mth.ceil((float)this.lastRecordedFoodLevel)); ++ this.updateScoreForCriteria(ObjectiveCriteria.FOOD, Mth.ceil((float) this.lastRecordedFoodLevel)); + } + + if (this.getAirSupply() != this.lastRecordedAirLevel) { + this.lastRecordedAirLevel = this.getAirSupply(); +- this.updateScoreForCriteria(ObjectiveCriteria.AIR, Mth.ceil((float)this.lastRecordedAirLevel)); ++ this.updateScoreForCriteria(ObjectiveCriteria.AIR, Mth.ceil((float) this.lastRecordedAirLevel)); + } + + if (this.getArmorValue() != this.lastRecordedArmor) { + this.lastRecordedArmor = this.getArmorValue(); +- this.updateScoreForCriteria(ObjectiveCriteria.ARMOR, Mth.ceil((float)this.lastRecordedArmor)); ++ this.updateScoreForCriteria(ObjectiveCriteria.ARMOR, Mth.ceil((float) this.lastRecordedArmor)); + } + + if (this.totalExperience != this.lastRecordedExperience) { + this.lastRecordedExperience = this.totalExperience; +- this.updateScoreForCriteria(ObjectiveCriteria.EXPERIENCE, Mth.ceil((float)this.lastRecordedExperience)); ++ this.updateScoreForCriteria(ObjectiveCriteria.EXPERIENCE, Mth.ceil((float) this.lastRecordedExperience)); + } + ++ // CraftBukkit start - Force max health updates ++ if (this.maxHealthCache != this.getMaxHealth()) { ++ this.getBukkitEntity().updateScaledHealth(); ++ } ++ // CraftBukkit end ++ + if (this.experienceLevel != this.lastRecordedLevel) { + this.lastRecordedLevel = this.experienceLevel; +- this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil((float)this.lastRecordedLevel)); ++ this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil((float) this.lastRecordedLevel)); + } + + if (this.totalExperience != this.lastSentExp) { +@@ -550,11 +730,27 @@ + if (this.tickCount % 20 == 0) { + CriteriaTriggers.LOCATION.trigger(this); + } +- } catch (Throwable var4) { +- CrashReport crashReport = CrashReport.forThrowable(var4, "Ticking player"); +- CrashReportCategory crashReportCategory = crashReport.addCategory("Player being ticked"); +- this.fillCrashReportCategory(crashReportCategory); +- throw new ReportedException(crashReport); ++ ++ // CraftBukkit start - initialize oldLevel, fire PlayerLevelChangeEvent, and tick client-sided world border ++ if (this.oldLevel == -1) { ++ this.oldLevel = this.experienceLevel; ++ } ++ ++ if (this.oldLevel != this.experienceLevel) { ++ CraftEventFactory.callPlayerLevelChangeEvent(this.getBukkitEntity(), this.oldLevel, this.experienceLevel); ++ this.oldLevel = this.experienceLevel; ++ } ++ ++ if (this.getBukkitEntity().hasClientWorldBorder()) { ++ ((CraftWorldBorder) this.getBukkitEntity().getWorldBorder()).getHandle().tick(); ++ } ++ // CraftBukkit end ++ } catch (Throwable throwable) { ++ CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking player"); ++ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Player being ticked"); ++ ++ this.fillCrashReportCategory(crashreportsystemdetails); ++ throw new ReportedException(crashreport); + } + } + +@@ -572,6 +768,7 @@ + if (this.fallDistance > 0.0F && this.startingToFallPosition == null) { + this.startingToFallPosition = this.position(); + } ++ + } + + public void trackEnteredOrExitedLavaOnVehicle() { +@@ -586,42 +783,83 @@ + if (this.enteredLavaOnVehiclePosition != null && (this.getVehicle() == null || !this.getVehicle().isInLava())) { + this.enteredLavaOnVehiclePosition = null; + } ++ + } + + private void updateScoreForCriteria(ObjectiveCriteria criteria, int points) { +- this.getScoreboard().forAllObjectives(criteria, this, scoreAccess -> scoreAccess.set(points)); ++ // CraftBukkit - Use our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteria, this, (scoreaccess) -> { ++ scoreaccess.set(points); ++ }); + } + + @Override + public void die(DamageSource cause) { + this.gameEvent(GameEvent.ENTITY_DIE); +- boolean _boolean = this.level().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); +- if (_boolean) { +- Component deathMessage = this.getCombatTracker().getDeathMessage(); +- this.connection +- .send( +- new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage), +- PacketSendListener.exceptionallySend( +- () -> { +- int i = 256; +- String string = deathMessage.getString(256); +- Component component = Component.translatable( +- "death.attack.message_too_long", Component.literal(string).withStyle(ChatFormatting.YELLOW) +- ); +- Component component1 = Component.translatable("death.attack.even_more_magic", this.getDisplayName()) +- .withStyle(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, component))); +- return new ClientboundPlayerCombatKillPacket(this.getId(), component1); +- } +- ) +- ); +- Team team = this.getTeam(); +- if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) { +- this.server.getPlayerList().broadcastSystemMessage(deathMessage, false); +- } else if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { +- this.server.getPlayerList().broadcastSystemToTeam(this, deathMessage); +- } else if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OWN_TEAM) { +- this.server.getPlayerList().broadcastSystemToAllExceptTeam(this, deathMessage); ++ boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); ++ // CraftBukkit start - fire PlayerDeathEvent ++ if (this.isRemoved()) { ++ return; ++ } ++ java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.getInventory().getContainerSize()); ++ boolean keepInventory = this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || this.isSpectator(); ++ ++ if (!keepInventory) { ++ for (ItemStack item : this.getInventory().getContents()) { ++ if (!item.isEmpty() && !EnchantmentHelper.hasVanishingCurse(item)) { ++ loot.add(CraftItemStack.asCraftMirror(item)); ++ } + } ++ } ++ // SPIGOT-5071: manually add player loot tables (SPIGOT-5195 - ignores keepInventory rule) ++ this.dropFromLootTable(cause, this.lastHurtByPlayerTime > 0); ++ for (org.bukkit.inventory.ItemStack item : this.drops) { ++ loot.add(item); ++ } ++ this.drops.clear(); // SPIGOT-5188: make sure to clear ++ ++ Component defaultMessage = this.getCombatTracker().getDeathMessage(); ++ ++ String deathmessage = defaultMessage.getString(); ++ keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel ++ org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory); ++ ++ // SPIGOT-943 - only call if they have an inventory open ++ if (this.containerMenu != this.inventoryMenu) { ++ this.closeContainer(); ++ } ++ ++ String deathMessage = event.getDeathMessage(); ++ ++ if (deathMessage != null && deathMessage.length() > 0 && flag) { // TODO: allow plugins to override? ++ Component ichatbasecomponent; ++ if (deathMessage.equals(deathmessage)) { ++ ichatbasecomponent = this.getCombatTracker().getDeathMessage(); ++ } else { ++ ichatbasecomponent = org.bukkit.craftbukkit.util.CraftChatMessage.fromStringOrNull(deathMessage); ++ } ++ ++ this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), ichatbasecomponent), PacketSendListener.exceptionallySend(() -> { ++ boolean flag1 = true; ++ String s = ichatbasecomponent.getString(256); ++ MutableComponent ichatmutablecomponent = Component.translatable("death.attack.message_too_long", Component.literal(s).withStyle(ChatFormatting.YELLOW)); ++ MutableComponent ichatmutablecomponent1 = Component.translatable("death.attack.even_more_magic", this.getDisplayName()).withStyle((chatmodifier) -> { ++ return chatmodifier.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, ichatmutablecomponent)); ++ }); ++ ++ return new ClientboundPlayerCombatKillPacket(this.getId(), ichatmutablecomponent1); ++ })); ++ PlayerTeam scoreboardteam = this.getTeam(); ++ ++ if (scoreboardteam != null && scoreboardteam.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { ++ if (scoreboardteam.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { ++ this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent); ++ } else if (scoreboardteam.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OWN_TEAM) { ++ this.server.getPlayerList().broadcastSystemToAllExceptTeam(this, ichatbasecomponent); ++ } ++ } else { ++ this.server.getPlayerList().broadcastSystemMessage(ichatbasecomponent, false); ++ } + } else { + this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), CommonComponents.EMPTY)); + } +@@ -630,20 +868,27 @@ + if (this.level().getGameRules().getBoolean(GameRules.RULE_FORGIVE_DEAD_PLAYERS)) { + this.tellNeutralMobsThatIDied(); + } +- +- if (!this.isSpectator()) { +- this.dropAllDeathLoot(cause); ++ // SPIGOT-5478 must be called manually now ++ this.dropExperience(); ++ // we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. ++ if (!event.getKeepInventory()) { ++ this.getInventory().clearContent(); + } + +- this.getScoreboard().forAllObjectives(ObjectiveCriteria.DEATH_COUNT, this, ScoreAccess::increment); +- LivingEntity killCredit = this.getKillCredit(); +- if (killCredit != null) { +- this.awardStat(Stats.ENTITY_KILLED_BY.get(killCredit.getType())); +- killCredit.awardKillScore(this, this.deathScore, cause); +- this.createWitherRose(killCredit); ++ this.setCamera(this); // Remove spectated target ++ // CraftBukkit end ++ ++ // CraftBukkit - Get our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.DEATH_COUNT, this, ScoreAccess::increment); ++ LivingEntity entityliving = this.getKillCredit(); ++ ++ if (entityliving != null) { ++ this.awardStat(Stats.ENTITY_KILLED_BY.get(entityliving.getType())); ++ entityliving.awardKillScore(this, this.deathScore, cause); ++ this.createWitherRose(entityliving); + } + +- this.level().broadcastEntityEvent(this, (byte)3); ++ this.level().broadcastEntityEvent(this, (byte) 3); + this.awardStat(Stats.DEATHS); + this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_DEATH)); + this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)); +@@ -655,12 +900,13 @@ + } + + private void tellNeutralMobsThatIDied() { +- AABB aABB = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0); +- this.level() +- .getEntitiesOfClass(Mob.class, aABB, EntitySelector.NO_SPECTATORS) +- .stream() +- .filter(mob -> mob instanceof NeutralMob) +- .forEach(mob -> ((NeutralMob)mob).playerDied(this)); ++ AABB axisalignedbb = (new AABB(this.blockPosition())).inflate(32.0D, 10.0D, 32.0D); ++ ++ this.level().getEntitiesOfClass(Mob.class, axisalignedbb, EntitySelector.NO_SPECTATORS).stream().filter((entityinsentient) -> { ++ return entityinsentient instanceof NeutralMob; ++ }).forEach((entityinsentient) -> { ++ ((NeutralMob) entityinsentient).playerDied(this); ++ }); + } + + @Override +@@ -668,10 +914,12 @@ + if (killed != this) { + super.awardKillScore(killed, scoreValue, damageSource); + this.increaseScore(scoreValue); +- this.getScoreboard().forAllObjectives(ObjectiveCriteria.KILL_COUNT_ALL, this, ScoreAccess::increment); ++ // CraftBukkit - Get our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.KILL_COUNT_ALL, this, ScoreAccess::increment); + if (killed instanceof Player) { + this.awardStat(Stats.PLAYER_KILLS); +- this.getScoreboard().forAllObjectives(ObjectiveCriteria.KILL_COUNT_PLAYERS, this, ScoreAccess::increment); ++ // CraftBukkit - Get our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(ObjectiveCriteria.KILL_COUNT_PLAYERS, this, ScoreAccess::increment); + } else { + this.awardStat(Stats.MOB_KILLS); + } +@@ -682,14 +930,18 @@ + } + } + +- private void handleTeamKill(ScoreHolder scoreHolder, ScoreHolder scoreHolder1, ObjectiveCriteria[] objectiveCriterias) { +- PlayerTeam playersTeam = this.getScoreboard().getPlayersTeam(scoreHolder1.getScoreboardName()); +- if (playersTeam != null) { +- int id = playersTeam.getColor().getId(); +- if (id >= 0 && id < objectiveCriterias.length) { +- this.getScoreboard().forAllObjectives(objectiveCriterias[id], scoreHolder, ScoreAccess::increment); ++ private void handleTeamKill(ScoreHolder scoreholder, ScoreHolder scoreholder1, ObjectiveCriteria[] aiscoreboardcriteria) { ++ PlayerTeam scoreboardteam = this.getScoreboard().getPlayersTeam(scoreholder1.getScoreboardName()); ++ ++ if (scoreboardteam != null) { ++ int i = scoreboardteam.getColor().getId(); ++ ++ if (i >= 0 && i < aiscoreboardcriteria.length) { ++ // CraftBukkit - Get our scores instead ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(aiscoreboardcriteria[i], scoreholder, ScoreAccess::increment); + } + } ++ + } + + @Override +@@ -698,16 +950,31 @@ + return false; + } else { + boolean flag = this.server.isDedicatedServer() && this.isPvpAllowed() && source.is(DamageTypeTags.IS_FALL); ++ + if (!flag && this.spawnInvulnerableTime > 0 && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { + return false; + } else { + Entity entity = source.getEntity(); +- if (entity instanceof Player player && !this.canHarmPlayer(player)) { +- return false; ++ ++ if (entity instanceof Player) { ++ Player entityhuman = (Player) entity; ++ ++ if (!this.canHarmPlayer(entityhuman)) { ++ return false; ++ } + } + +- if (entity instanceof AbstractArrow abstractArrow && abstractArrow.getOwner() instanceof Player player1 && !this.canHarmPlayer(player1)) { +- return false; ++ if (entity instanceof AbstractArrow) { ++ AbstractArrow entityarrow = (AbstractArrow) entity; ++ Entity entity1 = entityarrow.getOwner(); ++ ++ if (entity1 instanceof Player) { ++ Player entityhuman1 = (Player) entity1; ++ ++ if (!this.canHarmPlayer(entityhuman1)) { ++ return false; ++ } ++ } + } + + return super.hurt(source, amount); +@@ -717,32 +984,46 @@ + + @Override + public boolean canHarmPlayer(Player other) { +- return this.isPvpAllowed() && super.canHarmPlayer(other); ++ return !this.isPvpAllowed() ? false : super.canHarmPlayer(other); + } + + private boolean isPvpAllowed() { +- return this.server.isPvpAllowed(); ++ // CraftBukkit - this.server.isPvpAllowed() -> this.world.pvpMode ++ return this.level().pvpMode; + } + + @Nullable + @Override + protected PortalInfo findDimensionEntryPoint(ServerLevel destination) { +- PortalInfo portalInfo = super.findDimensionEntryPoint(destination); +- if (portalInfo != null && this.level().dimension() == Level.OVERWORLD && destination.dimension() == Level.END) { +- Vec3 vec3 = portalInfo.pos.add(0.0, -1.0, 0.0); +- return new PortalInfo(vec3, Vec3.ZERO, 90.0F, 0.0F); ++ PortalInfo shapedetectorshape = super.findDimensionEntryPoint(destination); ++ destination = (shapedetectorshape == null) ? destination : shapedetectorshape.world; // CraftBukkit ++ ++ if (shapedetectorshape != null && this.level().getTypeKey() == LevelStem.OVERWORLD && destination != null && destination.getTypeKey() == LevelStem.END) { // CraftBukkit ++ Vec3 vec3d = shapedetectorshape.pos.add(0.0D, -1.0D, 0.0D); ++ ++ return new PortalInfo(vec3d, Vec3.ZERO, 90.0F, 0.0F, destination, shapedetectorshape.portalEventInfo); // CraftBukkit + } else { +- return portalInfo; ++ return shapedetectorshape; + } + } + + @Nullable + @Override + public Entity changeDimension(ServerLevel server) { +- this.isChangingDimension = true; +- ServerLevel serverLevel = this.serverLevel(); +- ResourceKey<Level> resourceKey = serverLevel.dimension(); +- if (resourceKey == Level.END && server.dimension() == Level.OVERWORLD) { ++ // CraftBukkit start ++ return changeDimension(server, TeleportCause.UNKNOWN); ++ } ++ ++ @Nullable ++ public Entity changeDimension(ServerLevel worldserver, PlayerTeleportEvent.TeleportCause cause) { ++ // CraftBukkit end ++ if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154 ++ // this.isChangingDimension = true; // CraftBukkit - Moved down and into PlayerList#changeDimension ++ ServerLevel worldserver1 = this.serverLevel(); ++ ResourceKey<LevelStem> resourcekey = worldserver1.getTypeKey(); // CraftBukkit ++ ++ if (resourcekey == LevelStem.END && worldserver != null && worldserver.getTypeKey() == LevelStem.OVERWORLD) { // CraftBukkit ++ this.isChangingDimension = true; // CraftBukkit - Moved down from above + this.unRide(); + this.serverLevel().removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); + if (!this.wonGame) { +@@ -753,93 +1034,172 @@ + + return this; + } else { +- LevelData levelData = server.getLevelData(); +- this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(server), (byte)3)); +- this.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); +- PlayerList playerList = this.server.getPlayerList(); +- playerList.sendPlayerPermissionLevel(this); +- serverLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); ++ // CraftBukkit start ++ /* ++ WorldData worlddata = worldserver.getLevelData(); ++ ++ this.connection.send(new PacketPlayOutRespawn(this.createCommonSpawnInfo(worldserver), (byte) 3)); ++ this.connection.send(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); ++ PlayerList playerlist = this.server.getPlayerList(); ++ ++ playerlist.sendPlayerPermissionLevel(this); ++ worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); + this.unsetRemoved(); +- PortalInfo portalInfo = this.findDimensionEntryPoint(server); +- if (portalInfo != null) { +- serverLevel.getProfiler().push("moving"); +- if (resourceKey == Level.OVERWORLD && server.dimension() == Level.NETHER) { ++ */ ++ // CraftBukkit end ++ PortalInfo shapedetectorshape = this.findDimensionEntryPoint(worldserver); ++ ++ if (shapedetectorshape != null) { ++ worldserver1.getProfiler().push("moving"); ++ worldserver = shapedetectorshape.world; // CraftBukkit ++ if (worldserver == null) { } else // CraftBukkit - empty to fall through to null to event ++ if (resourcekey == LevelStem.OVERWORLD && worldserver.getTypeKey() == LevelStem.NETHER) { // CraftBukkit + this.enteredNetherPosition = this.position(); +- } else if (server.dimension() == Level.END) { +- this.createEndPlatform(server, BlockPos.containing(portalInfo.pos)); ++ } else if (worldserver.getTypeKey() == LevelStem.END && shapedetectorshape.portalEventInfo != null && shapedetectorshape.portalEventInfo.getCanCreatePortal()) { // CraftBukkit ++ this.createEndPlatform(worldserver, BlockPos.containing(shapedetectorshape.pos)); + } ++ // CraftBukkit start ++ } else { ++ return null; ++ } ++ Location enter = this.getBukkitEntity().getLocation(); ++ Location exit = (worldserver == null) ? null : CraftLocation.toBukkit(shapedetectorshape.pos, worldserver.getWorld(), shapedetectorshape.yRot, shapedetectorshape.xRot); ++ PlayerTeleportEvent tpEvent = new PlayerTeleportEvent(this.getBukkitEntity(), enter, exit, cause); ++ Bukkit.getServer().getPluginManager().callEvent(tpEvent); ++ if (tpEvent.isCancelled() || tpEvent.getTo() == null) { ++ return null; ++ } ++ exit = tpEvent.getTo(); ++ worldserver = ((CraftWorld) exit.getWorld()).getHandle(); ++ // CraftBukkit end + +- serverLevel.getProfiler().pop(); +- serverLevel.getProfiler().push("placing"); +- this.setServerLevel(server); +- this.connection.teleport(portalInfo.pos.x, portalInfo.pos.y, portalInfo.pos.z, portalInfo.yRot, portalInfo.xRot); ++ worldserver1.getProfiler().pop(); ++ worldserver1.getProfiler().push("placing"); ++ if (true) { // CraftBukkit ++ this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds ++ ++ this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(worldserver), (byte) 3)); ++ this.connection.send(new ClientboundChangeDifficultyPacket(this.level().getDifficulty(), this.level().getLevelData().isDifficultyLocked())); ++ PlayerList playerlist = this.server.getPlayerList(); ++ ++ playerlist.sendPlayerPermissionLevel(this); ++ worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); ++ this.unsetRemoved(); ++ ++ // CraftBukkit end ++ this.setServerLevel(worldserver); ++ this.connection.teleport(exit); // CraftBukkit - use internal teleport without event + this.connection.resetPosition(); +- server.addDuringPortalTeleport(this); +- serverLevel.getProfiler().pop(); +- this.triggerDimensionChangeTriggers(serverLevel); ++ worldserver.addDuringPortalTeleport(this); ++ worldserver1.getProfiler().pop(); ++ this.triggerDimensionChangeTriggers(worldserver1); + this.connection.send(new ClientboundPlayerAbilitiesPacket(this.getAbilities())); +- playerList.sendLevelInfo(this, server); +- playerList.sendAllPlayerInfo(this); ++ playerlist.sendLevelInfo(this, worldserver); ++ playerlist.sendAllPlayerInfo(this); ++ Iterator iterator = this.getActiveEffects().iterator(); + +- for (MobEffectInstance mobEffectInstance : this.getActiveEffects()) { +- this.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), mobEffectInstance)); ++ while (iterator.hasNext()) { ++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next(); ++ ++ this.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), mobeffect)); + } + + this.connection.send(new ClientboundLevelEventPacket(1032, BlockPos.ZERO, 0, false)); + this.lastSentExp = -1; + this.lastSentHealth = -1.0F; + this.lastSentFood = -1; ++ ++ // CraftBukkit start ++ PlayerChangedWorldEvent changeEvent = new PlayerChangedWorldEvent(this.getBukkitEntity(), worldserver1.getWorld()); ++ this.level().getCraftServer().getPluginManager().callEvent(changeEvent); ++ // CraftBukkit end + } + + return this; + } + } + ++ // CraftBukkit start ++ @Override ++ protected CraftPortalEvent callPortalEvent(Entity entity, ServerLevel exitWorldServer, Vec3 exitPosition, TeleportCause cause, int searchRadius, int creationRadius) { ++ Location enter = this.getBukkitEntity().getLocation(); ++ Location exit = CraftLocation.toBukkit(exitPosition, exitWorldServer.getWorld(), getYRot(), getXRot()); ++ PlayerPortalEvent event = new PlayerPortalEvent(this.getBukkitEntity(), enter, exit, cause, searchRadius, true, creationRadius); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null) { ++ return null; ++ } ++ return new CraftPortalEvent(event); ++ } ++ // CraftBukkit end ++ + private void createEndPlatform(ServerLevel level, BlockPos pos) { +- BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); ++ BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); ++ org.bukkit.craftbukkit.util.BlockStateListPopulator blockList = new org.bukkit.craftbukkit.util.BlockStateListPopulator(level); // CraftBukkit + +- for (int i = -2; i <= 2; i++) { +- for (int i1 = -2; i1 <= 2; i1++) { +- for (int i2 = -1; i2 < 3; i2++) { +- BlockState blockState = i2 == -1 ? Blocks.OBSIDIAN.defaultBlockState() : Blocks.AIR.defaultBlockState(); +- level.setBlockAndUpdate(mutableBlockPos.set(pos).move(i1, i2, i), blockState); ++ for (int i = -2; i <= 2; ++i) { ++ for (int j = -2; j <= 2; ++j) { ++ for (int k = -1; k < 3; ++k) { ++ IBlockData iblockdata = k == -1 ? Blocks.OBSIDIAN.defaultBlockState() : Blocks.AIR.defaultBlockState(); ++ ++ blockList.setBlock(blockposition_mutableblockposition.set(pos).move(j, k, i), iblockdata, 3); // CraftBukkit + } + } + } ++ // CraftBukkit start - call portal event ++ org.bukkit.event.world.PortalCreateEvent portalEvent = new org.bukkit.event.world.PortalCreateEvent((List<org.bukkit.block.BlockState>) (List) blockList.getList(), level.getWorld(), this.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.END_PLATFORM); ++ level.getCraftServer().getPluginManager().callEvent(portalEvent); ++ if (!portalEvent.isCancelled()) { ++ blockList.updateList(); ++ } ++ // CraftBukkit end ++ + } + + @Override +- protected Optional<BlockUtil.FoundRectangle> getExitPortal(ServerLevel destination, BlockPos findFrom, boolean isToNether, WorldBorder worldBorder) { +- Optional<BlockUtil.FoundRectangle> optional = super.getExitPortal(destination, findFrom, isToNether, worldBorder); +- if (optional.isPresent()) { ++ protected Optional<BlockUtil.FoundRectangle> getExitPortal(ServerLevel worldserver, BlockPos blockposition, boolean flag, WorldBorder worldborder, int searchRadius, boolean canCreatePortal, int createRadius) { // CraftBukkit ++ Optional<BlockUtil.FoundRectangle> optional = super.getExitPortal(worldserver, blockposition, flag, worldborder, searchRadius, canCreatePortal, createRadius); // CraftBukkit ++ ++ if (optional.isPresent() || !canCreatePortal) { // CraftBukkit + return optional; + } else { +- Direction.Axis axis = this.level().getBlockState(this.portalEntrancePos).getOptionalValue(NetherPortalBlock.AXIS).orElse(Direction.Axis.X); +- Optional<BlockUtil.FoundRectangle> optional1 = destination.getPortalForcer().createPortal(findFrom, axis); ++ Direction.Axis enumdirection_enumaxis = (Direction.Axis) this.level().getBlockState(this.portalEntrancePos).getOptionalValue(NetherPortalBlock.AXIS).orElse(Direction.Axis.X); ++ Optional<BlockUtil.FoundRectangle> optional1 = worldserver.getPortalForcer().createPortal(blockposition, enumdirection_enumaxis, this, createRadius); // CraftBukkit ++ + if (optional1.isEmpty()) { +- LOGGER.error("Unable to create a portal, likely target out of worldborder"); ++ // EntityPlayer.LOGGER.error("Unable to create a portal, likely target out of worldborder"); // CraftBukkit + } + + return optional1; + } + } + +- private void triggerDimensionChangeTriggers(ServerLevel level) { +- ResourceKey<Level> resourceKey = level.dimension(); +- ResourceKey<Level> resourceKey1 = this.level().dimension(); +- CriteriaTriggers.CHANGED_DIMENSION.trigger(this, resourceKey, resourceKey1); +- if (resourceKey == Level.NETHER && resourceKey1 == Level.OVERWORLD && this.enteredNetherPosition != null) { ++ public void triggerDimensionChangeTriggers(ServerLevel level) { ++ ResourceKey<Level> resourcekey = level.dimension(); ++ ResourceKey<Level> resourcekey1 = this.level().dimension(); ++ // CraftBukkit start ++ ResourceKey<Level> maindimensionkey = CraftDimensionUtil.getMainDimensionKey(level); ++ ResourceKey<Level> maindimensionkey1 = CraftDimensionUtil.getMainDimensionKey(this.level()); ++ ++ CriteriaTriggers.CHANGED_DIMENSION.trigger(this, maindimensionkey, maindimensionkey1); ++ if (maindimensionkey != resourcekey || maindimensionkey1 != resourcekey1) { ++ CriteriaTriggers.CHANGED_DIMENSION.trigger(this, resourcekey, resourcekey1); ++ } ++ ++ if (maindimensionkey == Level.NETHER && maindimensionkey1 == Level.OVERWORLD && this.enteredNetherPosition != null) { ++ // CraftBukkit end + CriteriaTriggers.NETHER_TRAVEL.trigger(this, this.enteredNetherPosition); + } + +- if (resourceKey1 != Level.NETHER) { ++ if (maindimensionkey1 != Level.NETHER) { // CraftBukkit + this.enteredNetherPosition = null; + } ++ + } + + @Override + public boolean broadcastToPlayer(ServerPlayer player) { +- return player.isSpectator() ? this.getCamera() == this : !this.isSpectator() && super.broadcastToPlayer(player); ++ return player.isSpectator() ? this.getCamera() == this : (this.isSpectator() ? false : super.broadcastToPlayer(player)); + } + + @Override +@@ -848,49 +1208,77 @@ + this.containerMenu.broadcastChanges(); + } + +- @Override +- public Either<Player.BedSleepingProblem, Unit> startSleepInBed(BlockPos at) { +- Direction direction = this.level().getBlockState(at).getValue(HorizontalDirectionalBlock.FACING); +- if (this.isSleeping() || !this.isAlive()) { +- return Either.left(Player.BedSleepingProblem.OTHER_PROBLEM); +- } else if (!this.level().dimensionType().natural()) { +- return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_HERE); +- } else if (!this.bedInRange(at, direction)) { +- return Either.left(Player.BedSleepingProblem.TOO_FAR_AWAY); +- } else if (this.bedBlocked(at, direction)) { +- return Either.left(Player.BedSleepingProblem.OBSTRUCTED); +- } else { +- this.setRespawnPosition(this.level().dimension(), at, this.getYRot(), false, true); +- if (this.level().isDay()) { +- return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_NOW); ++ // CraftBukkit start - moved bed result checks from below into separate method ++ private Either<Player.BedSleepingProblem, Unit> getBedResult(BlockPos blockposition, Direction enumdirection) { ++ if (!this.isSleeping() && this.isAlive()) { ++ if (!this.level().dimensionType().natural() || !this.level().dimensionType().bedWorks()) { ++ return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_HERE); ++ } else if (!this.bedInRange(blockposition, enumdirection)) { ++ return Either.left(Player.BedSleepingProblem.TOO_FAR_AWAY); ++ } else if (this.bedBlocked(blockposition, enumdirection)) { ++ return Either.left(Player.BedSleepingProblem.OBSTRUCTED); + } else { +- if (!this.isCreative()) { +- double d = 8.0; +- double d1 = 5.0; +- Vec3 vec3 = Vec3.atBottomCenterOf(at); +- List<Monster> entitiesOfClass = this.level() +- .getEntitiesOfClass( +- Monster.class, +- new AABB(vec3.x() - 8.0, vec3.y() - 5.0, vec3.z() - 8.0, vec3.x() + 8.0, vec3.y() + 5.0, vec3.z() + 8.0), +- monster -> monster.isPreventingPlayerRest(this) +- ); +- if (!entitiesOfClass.isEmpty()) { +- return Either.left(Player.BedSleepingProblem.NOT_SAFE); ++ this.setRespawnPosition(this.level().dimension(), blockposition, this.getYRot(), false, true, PlayerSpawnChangeEvent.Cause.BED); // CraftBukkit ++ if (this.level().isDay()) { ++ return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_NOW); ++ } else { ++ if (!this.isCreative()) { ++ double d0 = 8.0D; ++ double d1 = 5.0D; ++ Vec3 vec3d = Vec3.atBottomCenterOf(blockposition); ++ List<Monster> list = this.level().getEntitiesOfClass(Monster.class, new AABB(vec3d.x() - 8.0D, vec3d.y() - 5.0D, vec3d.z() - 8.0D, vec3d.x() + 8.0D, vec3d.y() + 5.0D, vec3d.z() + 8.0D), (entitymonster) -> { ++ return entitymonster.isPreventingPlayerRest(this); ++ }); ++ ++ if (!list.isEmpty()) { ++ return Either.left(Player.BedSleepingProblem.NOT_SAFE); ++ } + } +- } + +- Either<Player.BedSleepingProblem, Unit> either = super.startSleepInBed(at).ifRight(unit -> { +- this.awardStat(Stats.SLEEP_IN_BED); +- CriteriaTriggers.SLEPT_IN_BED.trigger(this); +- }); +- if (!this.serverLevel().canSleepThroughNights()) { +- this.displayClientMessage(Component.translatable("sleep.not_possible"), true); ++ return Either.right(Unit.INSTANCE); + } ++ } ++ } else { ++ return Either.left(Player.BedSleepingProblem.OTHER_PROBLEM); ++ } ++ } + +- ((ServerLevel)this.level()).updateSleepingPlayerList(); +- return either; ++ @Override ++ public Either<Player.BedSleepingProblem, Unit> startSleepInBed(BlockPos blockposition, boolean force) { ++ Direction enumdirection = (Direction) this.level().getBlockState(blockposition).getValue(HorizontalDirectionalBlock.FACING); ++ Either<Player.BedSleepingProblem, Unit> bedResult = this.getBedResult(blockposition, enumdirection); ++ ++ if (bedResult.left().orElse(null) == Player.BedSleepingProblem.OTHER_PROBLEM) { ++ return bedResult; // return immediately if the result is not bypassable by plugins ++ } ++ ++ if (force) { ++ bedResult = Either.right(Unit.INSTANCE); ++ } ++ ++ bedResult = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedEnterEvent(this, blockposition, bedResult); ++ if (bedResult.left().isPresent()) { ++ return bedResult; ++ } ++ ++ { ++ { ++ { ++ Either<Player.BedSleepingProblem, Unit> either = super.startSleepInBed(blockposition, force).ifRight((unit) -> { ++ this.awardStat(Stats.SLEEP_IN_BED); ++ CriteriaTriggers.SLEPT_IN_BED.trigger(this); ++ }); ++ ++ if (!this.serverLevel().canSleepThroughNights()) { ++ this.displayClientMessage(Component.translatable("sleep.not_possible"), true); ++ } ++ ++ ((ServerLevel) this.level()).updateSleepingPlayerList(); ++ return either; ++ } + } + } ++ // CraftBukkit end + } + + @Override +@@ -904,31 +1292,52 @@ + } + + private boolean isReachableBedBlock(BlockPos pos) { +- Vec3 vec3 = Vec3.atBottomCenterOf(pos); +- return Math.abs(this.getX() - vec3.x()) <= 3.0 && Math.abs(this.getY() - vec3.y()) <= 2.0 && Math.abs(this.getZ() - vec3.z()) <= 3.0; ++ Vec3 vec3d = Vec3.atBottomCenterOf(pos); ++ ++ return Math.abs(this.getX() - vec3d.x()) <= 3.0D && Math.abs(this.getY() - vec3d.y()) <= 2.0D && Math.abs(this.getZ() - vec3d.z()) <= 3.0D; + } + + private boolean bedBlocked(BlockPos pos, Direction direction) { +- BlockPos blockPos = pos.above(); +- return !this.freeAt(blockPos) || !this.freeAt(blockPos.relative(direction.getOpposite())); ++ BlockPos blockposition1 = pos.above(); ++ ++ return !this.freeAt(blockposition1) || !this.freeAt(blockposition1.relative(direction.getOpposite())); + } + + @Override + public void stopSleepInBed(boolean wakeImmediately, boolean updateLevelForSleepingPlayers) { ++ if (!this.isSleeping()) return; // CraftBukkit - Can't leave bed if not in one! ++ // CraftBukkit start - fire PlayerBedLeaveEvent ++ CraftPlayer player = this.getBukkitEntity(); ++ BlockPos bedPosition = this.getSleepingPos().orElse(null); ++ ++ org.bukkit.block.Block bed; ++ if (bedPosition != null) { ++ bed = this.level().getWorld().getBlockAt(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ()); ++ } else { ++ bed = this.level().getWorld().getBlockAt(player.getLocation()); ++ } ++ ++ PlayerBedLeaveEvent event = new PlayerBedLeaveEvent(player, bed, true); ++ this.level().getCraftServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + if (this.isSleeping()) { + this.serverLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(this, 2)); + } + + super.stopSleepInBed(wakeImmediately, updateLevelForSleepingPlayers); + if (this.connection != null) { +- this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); ++ this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot(), TeleportCause.EXIT_BED); // CraftBukkit + } ++ + } + + @Override +- public void dismountTo(double x, double y, double z) { ++ public void dismountTo(double x, double d1, double y) { + this.removeVehicle(); +- this.setPos(x, y, z); ++ this.setPos(x, d1, y); + } + + @Override +@@ -937,21 +1346,22 @@ + } + + @Override +- protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) { +- } ++ protected void checkFallDamage(double y, boolean flag, IBlockData onGround, BlockPos state) {} + + @Override + protected void onChangedBlock(BlockPos pos) { + if (!this.isSpectator()) { + super.onChangedBlock(pos); + } ++ + } + +- public void doCheckFallDamage(double movementX, double movementY, double movementZ, boolean onGround) { ++ public void doCheckFallDamage(double movementX, double d1, double movementY, boolean flag) { + if (!this.touchingUnloadedChunk()) { +- this.checkSupportingBlock(onGround, new Vec3(movementX, movementY, movementZ)); +- BlockPos onPosLegacy = this.getOnPosLegacy(); +- super.checkFallDamage(movementY, onGround, this.level().getBlockState(onPosLegacy), onPosLegacy); ++ this.checkSupportingBlock(flag, new Vec3(movementX, d1, movementY)); ++ BlockPos blockposition = this.getOnPosLegacy(); ++ ++ super.checkFallDamage(d1, flag, this.level().getBlockState(blockposition), blockposition); + } + } + +@@ -960,6 +1370,7 @@ + if (this.level().tickRateManager().runsNormally()) { + super.pushEntities(); + } ++ + } + + @Override +@@ -968,32 +1379,57 @@ + this.connection.send(new ClientboundOpenSignEditorPacket(signEntity.getBlockPos(), isFrontText)); + } + +- private void nextContainerCounter() { ++ public int nextContainerCounter() { // CraftBukkit - void -> int + this.containerCounter = this.containerCounter % 100 + 1; ++ return containerCounter; // CraftBukkit + } + + @Override +- public OptionalInt openMenu(@Nullable MenuProvider menu) { ++ public OptionalInt openMenu(@Nullable ITileInventory menu) { + if (menu == null) { + return OptionalInt.empty(); + } else { ++ // CraftBukkit start - SPIGOT-6552: Handle inventory closing in CraftEventFactory#callInventoryOpenEvent(...) ++ /* + if (this.containerMenu != this.inventoryMenu) { + this.closeContainer(); + } ++ */ ++ // CraftBukkit end + + this.nextContainerCounter(); +- AbstractContainerMenu abstractContainerMenu = menu.createMenu(this.containerCounter, this.getInventory(), this); +- if (abstractContainerMenu == null) { ++ AbstractContainerMenu container = menu.createMenu(this.containerCounter, this.getInventory(), this); ++ ++ // CraftBukkit start - Inventory open hook ++ if (container != null) { ++ container.setTitle(menu.getDisplayName()); ++ ++ boolean cancelled = false; ++ container = CraftEventFactory.callInventoryOpenEvent(this, container, cancelled); ++ if (container == null && !cancelled) { // Let pre-cancelled events fall through ++ // SPIGOT-5263 - close chest if cancelled ++ if (menu instanceof Container) { ++ ((Container) menu).stopOpen(this); ++ } else if (menu instanceof ChestBlock.DoubleInventory) { ++ // SPIGOT-5355 - double chests too :( ++ ((ChestBlock.DoubleInventory) menu).inventorylargechest.stopOpen(this); ++ } ++ return OptionalInt.empty(); ++ } ++ } ++ // CraftBukkit end ++ if (container == null) { + if (this.isSpectator()) { + this.displayClientMessage(Component.translatable("container.spectatorCantOpen").withStyle(ChatFormatting.RED), true); + } + + return OptionalInt.empty(); + } else { +- this.connection +- .send(new ClientboundOpenScreenPacket(abstractContainerMenu.containerId, abstractContainerMenu.getType(), menu.getDisplayName())); +- this.initMenu(abstractContainerMenu); +- this.containerMenu = abstractContainerMenu; ++ // CraftBukkit start ++ this.containerMenu = container; ++ this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); ++ // CraftBukkit end ++ this.initMenu(container); + return OptionalInt.of(this.containerCounter); + } + } +@@ -1006,18 +1442,29 @@ + + @Override + public void openHorseInventory(AbstractHorse horse, Container inventory) { ++ // CraftBukkit start - Inventory open hook ++ this.nextContainerCounter(); ++ AbstractContainerMenu container = new HorseInventoryMenu(this.containerCounter, this.getInventory(), inventory, horse); ++ container.setTitle(horse.getDisplayName()); ++ container = CraftEventFactory.callInventoryOpenEvent(this, container); ++ ++ if (container == null) { ++ inventory.stopOpen(this); ++ return; ++ } ++ // CraftBukkit end + if (this.containerMenu != this.inventoryMenu) { + this.closeContainer(); + } + +- this.nextContainerCounter(); ++ // this.nextContainerCounter(); // CraftBukkit - moved up + this.connection.send(new ClientboundHorseScreenOpenPacket(this.containerCounter, inventory.getContainerSize(), horse.getId())); +- this.containerMenu = new HorseInventoryMenu(this.containerCounter, this.getInventory(), inventory, horse); ++ this.containerMenu = container; // CraftBukkit + this.initMenu(this.containerMenu); + } + + @Override +- public void openItemGui(ItemStack stack, InteractionHand hand) { ++ public void openItemGui(ItemStack stack, EnumHand hand) { + if (stack.is(Items.WRITTEN_BOOK)) { + if (WrittenBookItem.resolveBookComponents(stack, this.createCommandSourceStack(), this)) { + this.containerMenu.broadcastChanges(); +@@ -1025,6 +1472,7 @@ + + this.connection.send(new ClientboundOpenBookPacket(hand)); + } ++ + } + + @Override +@@ -1034,6 +1482,7 @@ + + @Override + public void closeContainer() { ++ CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit + this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); + this.doCloseContainer(); + } +@@ -1056,110 +1505,130 @@ + } + + this.jumping = jumping; ++ // CraftBukkit start ++ if (sneaking != this.isShiftKeyDown()) { ++ PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getBukkitEntity(), sneaking); ++ this.server.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end + this.setShiftKeyDown(sneaking); + } ++ + } + + @Override + public void travel(Vec3 travelVector) { +- double x = this.getX(); +- double y = this.getY(); +- double z = this.getZ(); ++ double d0 = this.getX(); ++ double d1 = this.getY(); ++ double d2 = this.getZ(); ++ + super.travel(travelVector); +- this.checkMovementStatistics(this.getX() - x, this.getY() - y, this.getZ() - z); ++ this.checkMovementStatistics(this.getX() - d0, this.getY() - d1, this.getZ() - d2); + } + + @Override + public void rideTick() { +- double x = this.getX(); +- double y = this.getY(); +- double z = this.getZ(); ++ double d0 = this.getX(); ++ double d1 = this.getY(); ++ double d2 = this.getZ(); ++ + super.rideTick(); +- this.checkRidingStatistics(this.getX() - x, this.getY() - y, this.getZ() - z); ++ this.checkRidingStatistics(this.getX() - d0, this.getY() - d1, this.getZ() - d2); + } + +- public void checkMovementStatistics(double d, double d1, double d2) { +- if (!this.isPassenger() && !didNotMove(d, d1, d2)) { ++ public void checkMovementStatistics(double d0, double d1, double d2) { ++ if (!this.isPassenger() && !didNotMove(d0, d1, d2)) { ++ int i; ++ + if (this.isSwimming()) { +- int rounded = Math.round((float)Math.sqrt(d * d + d1 * d1 + d2 * d2) * 100.0F); +- if (rounded > 0) { +- this.awardStat(Stats.SWIM_ONE_CM, rounded); +- this.causeFoodExhaustion(0.01F * (float)rounded * 0.01F); ++ i = Math.round((float) Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); ++ if (i > 0) { ++ this.awardStat(Stats.SWIM_ONE_CM, i); ++ this.causeFoodExhaustion(0.01F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.SWIM); // CraftBukkit - EntityExhaustionEvent + } + } else if (this.isEyeInFluid(FluidTags.WATER)) { +- int rounded = Math.round((float)Math.sqrt(d * d + d1 * d1 + d2 * d2) * 100.0F); +- if (rounded > 0) { +- this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, rounded); +- this.causeFoodExhaustion(0.01F * (float)rounded * 0.01F); ++ i = Math.round((float) Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); ++ if (i > 0) { ++ this.awardStat(Stats.WALK_UNDER_WATER_ONE_CM, i); ++ this.causeFoodExhaustion(0.01F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.WALK_UNDERWATER); // CraftBukkit - EntityExhaustionEvent + } + } else if (this.isInWater()) { +- int rounded = Math.round((float)Math.sqrt(d * d + d2 * d2) * 100.0F); +- if (rounded > 0) { +- this.awardStat(Stats.WALK_ON_WATER_ONE_CM, rounded); +- this.causeFoodExhaustion(0.01F * (float)rounded * 0.01F); ++ i = Math.round((float) Math.sqrt(d0 * d0 + d2 * d2) * 100.0F); ++ if (i > 0) { ++ this.awardStat(Stats.WALK_ON_WATER_ONE_CM, i); ++ this.causeFoodExhaustion(0.01F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.WALK_ON_WATER); // CraftBukkit - EntityExhaustionEvent + } + } else if (this.onClimbable()) { +- if (d1 > 0.0) { +- this.awardStat(Stats.CLIMB_ONE_CM, (int)Math.round(d1 * 100.0)); ++ if (d1 > 0.0D) { ++ this.awardStat(Stats.CLIMB_ONE_CM, (int) Math.round(d1 * 100.0D)); + } + } else if (this.onGround()) { +- int rounded = Math.round((float)Math.sqrt(d * d + d2 * d2) * 100.0F); +- if (rounded > 0) { ++ i = Math.round((float) Math.sqrt(d0 * d0 + d2 * d2) * 100.0F); ++ if (i > 0) { + if (this.isSprinting()) { +- this.awardStat(Stats.SPRINT_ONE_CM, rounded); +- this.causeFoodExhaustion(0.1F * (float)rounded * 0.01F); ++ this.awardStat(Stats.SPRINT_ONE_CM, i); ++ this.causeFoodExhaustion(0.1F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.SPRINT); // CraftBukkit - EntityExhaustionEvent + } else if (this.isCrouching()) { +- this.awardStat(Stats.CROUCH_ONE_CM, rounded); +- this.causeFoodExhaustion(0.0F * (float)rounded * 0.01F); ++ this.awardStat(Stats.CROUCH_ONE_CM, i); ++ this.causeFoodExhaustion(0.0F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.CROUCH); // CraftBukkit - EntityExhaustionEvent + } else { +- this.awardStat(Stats.WALK_ONE_CM, rounded); +- this.causeFoodExhaustion(0.0F * (float)rounded * 0.01F); ++ this.awardStat(Stats.WALK_ONE_CM, i); ++ this.causeFoodExhaustion(0.0F * (float) i * 0.01F, EntityExhaustionEvent.ExhaustionReason.WALK); // CraftBukkit - EntityExhaustionEvent + } + } + } else if (this.isFallFlying()) { +- int rounded = Math.round((float)Math.sqrt(d * d + d1 * d1 + d2 * d2) * 100.0F); +- this.awardStat(Stats.AVIATE_ONE_CM, rounded); ++ i = Math.round((float) Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); ++ this.awardStat(Stats.AVIATE_ONE_CM, i); + } else { +- int rounded = Math.round((float)Math.sqrt(d * d + d2 * d2) * 100.0F); +- if (rounded > 25) { +- this.awardStat(Stats.FLY_ONE_CM, rounded); ++ i = Math.round((float) Math.sqrt(d0 * d0 + d2 * d2) * 100.0F); ++ if (i > 25) { ++ this.awardStat(Stats.FLY_ONE_CM, i); + } + } ++ + } + } + +- private void checkRidingStatistics(double d, double d1, double d2) { +- if (this.isPassenger() && !didNotMove(d, d1, d2)) { +- int rounded = Math.round((float)Math.sqrt(d * d + d1 * d1 + d2 * d2) * 100.0F); +- Entity vehicle = this.getVehicle(); +- if (vehicle instanceof AbstractMinecart) { +- this.awardStat(Stats.MINECART_ONE_CM, rounded); +- } else if (vehicle instanceof Boat) { +- this.awardStat(Stats.BOAT_ONE_CM, rounded); +- } else if (vehicle instanceof Pig) { +- this.awardStat(Stats.PIG_ONE_CM, rounded); +- } else if (vehicle instanceof AbstractHorse) { +- this.awardStat(Stats.HORSE_ONE_CM, rounded); +- } else if (vehicle instanceof Strider) { +- this.awardStat(Stats.STRIDER_ONE_CM, rounded); ++ private void checkRidingStatistics(double d0, double d1, double d2) { ++ if (this.isPassenger() && !didNotMove(d0, d1, d2)) { ++ int i = Math.round((float) Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); ++ Entity entity = this.getVehicle(); ++ ++ if (entity instanceof AbstractMinecart) { ++ this.awardStat(Stats.MINECART_ONE_CM, i); ++ } else if (entity instanceof Boat) { ++ this.awardStat(Stats.BOAT_ONE_CM, i); ++ } else if (entity instanceof Pig) { ++ this.awardStat(Stats.PIG_ONE_CM, i); ++ } else if (entity instanceof AbstractHorse) { ++ this.awardStat(Stats.HORSE_ONE_CM, i); ++ } else if (entity instanceof Strider) { ++ this.awardStat(Stats.STRIDER_ONE_CM, i); + } ++ + } + } + +- private static boolean didNotMove(double d, double d1, double d2) { +- return d == 0.0 && d1 == 0.0 && d2 == 0.0; ++ private static boolean didNotMove(double d0, double d1, double d2) { ++ return d0 == 0.0D && d1 == 0.0D && d2 == 0.0D; + } + + @Override + public void awardStat(Stat<?> stat, int amount) { + this.stats.increment(this, stat, amount); +- this.getScoreboard().forAllObjectives(stat, this, scoreAccess -> scoreAccess.add(amount)); ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(stat, this, (scoreaccess) -> { ++ scoreaccess.add(amount); ++ }); + } + + @Override + public void resetStat(Stat<?> stat) { + this.stats.setValue(this, stat, 0); +- this.getScoreboard().forAllObjectives(stat, this, ScoreAccess::reset); ++ this.level().getCraftServer().getScoreboardManager().forAllObjectives(stat, this, ScoreAccess::reset); // CraftBukkit - Get our scores instead + } + + @Override +@@ -1168,15 +1637,16 @@ + } + + @Override +- public void triggerRecipeCrafted(RecipeHolder<?> recipeHolder, List<ItemStack> list) { +- CriteriaTriggers.RECIPE_CRAFTED.trigger(this, recipeHolder.id(), list); ++ public void triggerRecipeCrafted(RecipeHolder<?> recipeholder, List<ItemStack> list) { ++ CriteriaTriggers.RECIPE_CRAFTED.trigger(this, recipeholder.id(), list); + } + + @Override + public void awardRecipesByKey(List<ResourceLocation> list) { +- List<RecipeHolder<?>> list1 = list.stream() +- .flatMap(resourceLocation -> this.server.getRecipeManager().byKey(resourceLocation).stream()) +- .collect(Collectors.toList()); ++ List<RecipeHolder<?>> list1 = (List) list.stream().flatMap((minecraftkey) -> { ++ return this.server.getRecipeManager().byKey(minecraftkey).stream(); ++ }).collect(Collectors.toList()); ++ + this.awardRecipes(list1); + } + +@@ -1197,6 +1667,7 @@ + if (this.isSleeping()) { + this.stopSleepInBed(true, false); + } ++ + } + + public boolean hasDisconnected() { +@@ -1205,6 +1676,7 @@ + + public void resetSentInfo() { + this.lastSentHealth = -1.0E8F; ++ this.lastSentExp = -1; // CraftBukkit - Added to reset + } + + @Override +@@ -1215,9 +1687,10 @@ + @Override + protected void completeUsingItem() { + if (!this.useItem.isEmpty() && this.isUsingItem()) { +- this.connection.send(new ClientboundEntityEventPacket(this, (byte)9)); ++ this.connection.send(new ClientboundEntityEventPacket(this, (byte) 9)); + super.completeUsingItem(); + } ++ + } + + @Override +@@ -1227,8 +1700,9 @@ + } + + public void lookAt(EntityAnchorArgument.Anchor fromAnchor, Entity entity, EntityAnchorArgument.Anchor toAnchor) { +- Vec3 vec3 = toAnchor.apply(entity); +- super.lookAt(fromAnchor, vec3); ++ Vec3 vec3d = toAnchor.apply(entity); ++ ++ super.lookAt(fromAnchor, vec3d); + this.connection.send(new ClientboundPlayerLookAtPacket(fromAnchor, entity, toAnchor)); + } + +@@ -1256,11 +1730,11 @@ + + this.enchantmentSeed = that.enchantmentSeed; + this.enderChestInventory = that.enderChestInventory; +- this.getEntityData().set(DATA_PLAYER_MODE_CUSTOMISATION, that.getEntityData().get(DATA_PLAYER_MODE_CUSTOMISATION)); ++ this.getEntityData().set(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION, (Byte) that.getEntityData().get(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION)); + this.lastSentExp = -1; + this.lastSentHealth = -1.0F; + this.lastSentFood = -1; +- this.recipeBook.copyOverData(that.recipeBook); ++ // this.recipeBook.copyOverData(entityplayer.recipeBook); // CraftBukkit + this.seenCredits = that.seenCredits; + this.enteredNetherPosition = that.enteredNetherPosition; + this.chunkTrackingView = that.chunkTrackingView; +@@ -1296,41 +1770,48 @@ + this.levitationStartPos = null; + } + +- CriteriaTriggers.EFFECTS_CHANGED.trigger(this, null); ++ CriteriaTriggers.EFFECTS_CHANGED.trigger(this, (Entity) null); + } + + @Override +- public void teleportTo(double x, double y, double z) { +- this.connection.teleport(x, y, z, this.getYRot(), this.getXRot(), RelativeMovement.ROTATION); ++ public void teleportTo(double x, double d1, double y) { ++ this.connection.teleport(x, d1, y, this.getYRot(), this.getXRot(), RelativeMovement.ROTATION); + } + + @Override +- public void teleportRelative(double dx, double dy, double dz) { +- this.connection.teleport(this.getX() + dx, this.getY() + dy, this.getZ() + dz, this.getYRot(), this.getXRot(), RelativeMovement.ALL); ++ public void teleportRelative(double dx, double d1, double dy) { ++ this.connection.teleport(this.getX() + dx, this.getY() + d1, this.getZ() + dy, this.getYRot(), this.getXRot(), RelativeMovement.ALL); + } + + @Override +- public boolean teleportTo(ServerLevel level, double x, double y, double z, Set<RelativeMovement> relativeMovements, float yRot, float xRot) { +- ChunkPos chunkPos = new ChunkPos(BlockPos.containing(x, y, z)); +- level.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkPos, 1, this.getId()); ++ public boolean teleportTo(ServerLevel level, double x, double d1, double y, Set<RelativeMovement> set, float z, float f1) { ++ // CraftBukkit start ++ return teleportTo(level, x, d1, y, set, z, f1, TeleportCause.UNKNOWN); ++ } ++ ++ public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set<RelativeMovement> set, float f, float f1, TeleportCause cause) { ++ // CraftBukkit end ++ ChunkPos chunkcoordintpair = new ChunkPos(BlockPos.containing(d0, d1, d2)); ++ ++ worldserver.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkcoordintpair, 1, this.getId()); + this.stopRiding(); + if (this.isSleeping()) { + this.stopSleepInBed(true, true); + } + +- if (level == this.level()) { +- this.connection.teleport(x, y, z, yRot, xRot, relativeMovements); ++ if (worldserver == this.level()) { ++ this.connection.teleport(d0, d1, d2, f, f1, set, cause); // CraftBukkit + } else { +- this.teleportTo(level, x, y, z, yRot, xRot); ++ this.teleportTo(worldserver, d0, d1, d2, f, f1, cause); // CraftBukkit + } + +- this.setYHeadRot(yRot); ++ this.setYHeadRot(f); + return true; + } + + @Override +- public void moveTo(double x, double y, double z) { +- super.moveTo(x, y, z); ++ public void moveTo(double x, double d1, double y) { ++ super.moveTo(x, d1, y); + this.connection.resetPosition(); + } + +@@ -1353,14 +1834,14 @@ + } + + public ServerLevel serverLevel() { +- return (ServerLevel)this.level(); ++ return (ServerLevel) this.level(); + } + + public boolean setGameMode(GameType gameMode) { + if (!this.gameMode.changeGameModeForPlayer(gameMode)) { + return false; + } else { +- this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float)gameMode.getId())); ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float) gameMode.getId())); + if (gameMode == GameType.SPECTATOR) { + this.removeEntitiesOnShoulder(); + this.stopRiding(); +@@ -1391,24 +1872,17 @@ + + public void sendSystemMessage(Component component, boolean bypassHiddenChat) { + if (this.acceptsSystemMessages(bypassHiddenChat)) { +- this.connection +- .send( +- new ClientboundSystemChatPacket(component, bypassHiddenChat), +- PacketSendListener.exceptionallySend( +- () -> { +- if (this.acceptsSystemMessages(false)) { +- int i = 256; +- String string = component.getString(256); +- Component component1 = Component.literal(string).withStyle(ChatFormatting.YELLOW); +- return new ClientboundSystemChatPacket( +- Component.translatable("multiplayer.message_not_delivered", component1).withStyle(ChatFormatting.RED), false +- ); +- } else { +- return null; +- } +- } +- ) +- ); ++ this.connection.send(new ClientboundSystemChatPacket(component, bypassHiddenChat), PacketSendListener.exceptionallySend(() -> { ++ if (this.acceptsSystemMessages(false)) { ++ boolean flag1 = true; ++ String s = component.getString(256); ++ MutableComponent ichatmutablecomponent = Component.literal(s).withStyle(ChatFormatting.YELLOW); ++ ++ return new ClientboundSystemChatPacket(Component.translatable("multiplayer.message_not_delivered", ichatmutablecomponent).withStyle(ChatFormatting.RED), false); ++ } else { ++ return null; ++ } ++ })); + } + } + +@@ -1416,31 +1890,47 @@ + if (this.acceptsChatMessages()) { + message.sendToPlayer(this, filtered, boundType); + } ++ + } + + public String getIpAddress() { +- return this.connection.getRemoteAddress() instanceof InetSocketAddress inetSocketAddress +- ? InetAddresses.toAddrString(inetSocketAddress.getAddress()) +- : "<unknown>"; ++ SocketAddress socketaddress = this.connection.getRemoteAddress(); ++ ++ if (socketaddress instanceof InetSocketAddress) { ++ InetSocketAddress inetsocketaddress = (InetSocketAddress) socketaddress; ++ ++ return InetAddresses.toAddrString(inetsocketaddress.getAddress()); ++ } else { ++ return "<unknown>"; ++ } + } + +- public void updateOptions(ClientInformation clientInformation) { +- this.language = clientInformation.language(); +- this.requestedViewDistance = clientInformation.viewDistance(); +- this.chatVisibility = clientInformation.chatVisibility(); +- this.canChatColor = clientInformation.chatColors(); +- this.textFilteringEnabled = clientInformation.textFilteringEnabled(); +- this.allowsListing = clientInformation.allowsListing(); +- this.getEntityData().set(DATA_PLAYER_MODE_CUSTOMISATION, (byte)clientInformation.modelCustomisation()); +- this.getEntityData().set(DATA_PLAYER_MAIN_HAND, (byte)clientInformation.mainHand().getId()); ++ public void updateOptions(ClientInformation clientinformation) { ++ // CraftBukkit start ++ if (getMainArm() != clientinformation.mainHand()) { ++ PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(getBukkitEntity(), getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT); ++ this.server.server.getPluginManager().callEvent(event); ++ } ++ if (!this.language.equals(clientinformation.language())) { ++ PlayerLocaleChangeEvent event = new PlayerLocaleChangeEvent(getBukkitEntity(), clientinformation.language()); ++ this.server.server.getPluginManager().callEvent(event); ++ } ++ // CraftBukkit end ++ this.language = clientinformation.language(); ++ this.requestedViewDistance = clientinformation.viewDistance(); ++ this.chatVisibility = clientinformation.chatVisibility(); ++ this.canChatColor = clientinformation.chatColors(); ++ this.textFilteringEnabled = clientinformation.textFilteringEnabled(); ++ this.allowsListing = clientinformation.allowsListing(); ++ this.getEntityData().set(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION, (byte) clientinformation.modelCustomisation()); ++ this.getEntityData().set(ServerPlayer.DATA_PLAYER_MAIN_HAND, (byte) clientinformation.mainHand().getId()); + } + + public ClientInformation clientInformation() { +- int i = this.getEntityData().get(DATA_PLAYER_MODE_CUSTOMISATION); +- HumanoidArm humanoidArm = HumanoidArm.BY_ID.apply(this.getEntityData().get(DATA_PLAYER_MAIN_HAND)); +- return new ClientInformation( +- this.language, this.requestedViewDistance, this.chatVisibility, this.canChatColor, i, humanoidArm, this.textFilteringEnabled, this.allowsListing +- ); ++ byte b0 = (Byte) this.getEntityData().get(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION); ++ HumanoidArm enummainhand = (HumanoidArm) HumanoidArm.BY_ID.apply((Byte) this.getEntityData().get(ServerPlayer.DATA_PLAYER_MAIN_HAND)); ++ ++ return new ClientInformation(this.language, this.requestedViewDistance, this.chatVisibility, this.canChatColor, b0, enummainhand, this.textFilteringEnabled, this.allowsListing); + } + + public boolean canChatInColor() { +@@ -1452,7 +1942,7 @@ + } + + private boolean acceptsSystemMessages(boolean bypassHiddenChat) { +- return this.chatVisibility != ChatVisiblity.HIDDEN || bypassHiddenChat; ++ return this.chatVisibility == ChatVisiblity.HIDDEN ? bypassHiddenChat : true; + } + + private boolean acceptsChatMessages() { +@@ -1464,12 +1954,7 @@ + } + + public void sendServerStatus(ServerStatus serverStatus) { +- this.connection +- .send( +- new ClientboundServerDataPacket( +- serverStatus.description(), serverStatus.favicon().map(ServerStatus.Favicon::iconBytes), serverStatus.enforcesSecureChat() +- ) +- ); ++ this.connection.send(new ClientboundServerDataPacket(serverStatus.description(), serverStatus.favicon().map(ServerStatus.a::iconBytes), serverStatus.enforcesSecureChat())); + } + + @Override +@@ -1497,18 +1982,24 @@ + } else { + super.updateInvisibilityStatus(); + } ++ + } + + public Entity getCamera() { +- return (Entity)(this.camera == null ? this : this.camera); ++ return (Entity) (this.camera == null ? this : this.camera); + } + + public void setCamera(@Nullable Entity entityToSpectate) { +- Entity camera = this.getCamera(); +- this.camera = (Entity)(entityToSpectate == null ? this : entityToSpectate); +- if (camera != this.camera) { +- if (this.camera.level() instanceof ServerLevel serverLevel) { +- this.teleportTo(serverLevel, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot()); ++ Entity entity1 = this.getCamera(); ++ ++ this.camera = (Entity) (entityToSpectate == null ? this : entityToSpectate); ++ if (entity1 != this.camera) { ++ Level world = this.camera.level(); ++ ++ if (world instanceof ServerLevel) { ++ ServerLevel worldserver = (ServerLevel) world; ++ ++ this.teleportTo(worldserver, this.camera.getX(), this.camera.getY(), this.camera.getZ(), Set.of(), this.getYRot(), this.getXRot(), TeleportCause.SPECTATE); // CraftBukkit + } + + if (entityToSpectate != null) { +@@ -1518,6 +2009,7 @@ + this.connection.send(new ClientboundSetCameraPacket(this.camera)); + this.connection.resetPosition(); + } ++ + } + + @Override +@@ -1525,6 +2017,7 @@ + if (!this.isChangingDimension) { + super.processPortalCooldown(); + } ++ + } + + @Override +@@ -1534,6 +2027,7 @@ + } else { + super.attack(targetEntity); + } ++ + } + + public long getLastActionTime() { +@@ -1542,11 +2036,11 @@ + + @Nullable + public Component getTabListDisplayName() { +- return null; ++ return listName; // CraftBukkit + } + + @Override +- public void swing(InteractionHand hand) { ++ public void swing(EnumHand hand) { + super.swing(hand); + this.resetAttackStrengthTicker(); + } +@@ -1563,27 +2057,39 @@ + return this.advancements; + } + +- public void teleportTo(ServerLevel newLevel, double x, double y, double z, float yaw, float pitch) { ++ // CraftBukkit start ++ public void teleportTo(ServerLevel newLevel, double x, double d1, double y, float f, float z) { ++ this.teleportTo(newLevel, x, d1, y, f, z, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ } ++ ++ public void teleportTo(ServerLevel worldserver, double d0, double d1, double d2, float f, float f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { ++ // CraftBukkit end + this.setCamera(this); + this.stopRiding(); +- if (newLevel == this.level()) { +- this.connection.teleport(x, y, z, yaw, pitch); ++ /* CraftBukkit start - replace with bukkit handling for multi-world ++ if (worldserver == this.level()) { ++ this.connection.teleport(d0, d1, d2, f, f1); + } else { +- ServerLevel serverLevel = this.serverLevel(); +- LevelData levelData = newLevel.getLevelData(); +- this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(newLevel), (byte)3)); +- this.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); ++ WorldServer worldserver1 = this.serverLevel(); ++ WorldData worlddata = worldserver.getLevelData(); ++ ++ this.connection.send(new PacketPlayOutRespawn(this.createCommonSpawnInfo(worldserver), (byte) 3)); ++ this.connection.send(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); + this.server.getPlayerList().sendPlayerPermissionLevel(this); +- serverLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); ++ worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); + this.unsetRemoved(); +- this.moveTo(x, y, z, yaw, pitch); +- this.setServerLevel(newLevel); +- newLevel.addDuringCommandTeleport(this); +- this.triggerDimensionChangeTriggers(serverLevel); +- this.connection.teleport(x, y, z, yaw, pitch); +- this.server.getPlayerList().sendLevelInfo(this, newLevel); ++ this.moveTo(d0, d1, d2, f, f1); ++ this.setServerLevel(worldserver); ++ worldserver.addDuringCommandTeleport(this); ++ this.triggerDimensionChangeTriggers(worldserver1); ++ this.connection.teleport(d0, d1, d2, f, f1); ++ this.server.getPlayerList().sendLevelInfo(this, worldserver); + this.server.getPlayerList().sendAllPlayerInfo(this); + } ++ */ ++ this.getBukkitEntity().teleport(new Location(worldserver.getWorld(), d0, d1, d2, f, f1), cause); ++ // CraftBukkit end ++ + } + + @Nullable +@@ -1604,22 +2110,50 @@ + } + + public void setRespawnPosition(ResourceKey<Level> dimension, @Nullable BlockPos position, float angle, boolean forced, boolean sendMessage) { +- if (position != null) { +- boolean flag = position.equals(this.respawnPosition) && dimension.equals(this.respawnDimension); +- if (sendMessage && !flag) { ++ // CraftBukkit start ++ this.setRespawnPosition(dimension, position, angle, forced, sendMessage, PlayerSpawnChangeEvent.Cause.UNKNOWN); ++ } ++ ++ public void setRespawnPosition(ResourceKey<Level> resourcekey, @Nullable BlockPos blockposition, float f, boolean flag, boolean flag1, PlayerSpawnChangeEvent.Cause cause) { ++ ServerLevel newWorld = this.server.getLevel(resourcekey); ++ Location newSpawn = (blockposition != null) ? CraftLocation.toBukkit(blockposition, newWorld.getWorld(), f, 0) : null; ++ ++ PlayerSpawnChangeEvent event = new PlayerSpawnChangeEvent(this.getBukkitEntity(), newSpawn, flag, cause); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ newSpawn = event.getNewSpawn(); ++ flag = event.isForced(); ++ ++ if (newSpawn != null) { ++ resourcekey = ((CraftWorld) newSpawn.getWorld()).getHandle().dimension(); ++ blockposition = BlockPos.containing(newSpawn.getX(), newSpawn.getY(), newSpawn.getZ()); ++ f = newSpawn.getYaw(); ++ } else { ++ resourcekey = Level.OVERWORLD; ++ blockposition = null; ++ f = 0.0F; ++ } ++ // CraftBukkit end ++ if (blockposition != null) { ++ boolean flag2 = blockposition.equals(this.respawnPosition) && resourcekey.equals(this.respawnDimension); ++ ++ if (flag1 && !flag2) { + this.sendSystemMessage(Component.translatable("block.minecraft.set_spawn")); + } + +- this.respawnPosition = position; +- this.respawnDimension = dimension; +- this.respawnAngle = angle; +- this.respawnForced = forced; ++ this.respawnPosition = blockposition; ++ this.respawnDimension = resourcekey; ++ this.respawnAngle = f; ++ this.respawnForced = flag; + } else { + this.respawnPosition = null; + this.respawnDimension = Level.OVERWORLD; + this.respawnAngle = 0.0F; + this.respawnForced = false; + } ++ + } + + public SectionPos getLastSectionPos() { +@@ -1634,37 +2168,34 @@ + return this.chunkTrackingView; + } + +- public void setChunkTrackingView(ChunkTrackingView chunkTrackingView) { +- this.chunkTrackingView = chunkTrackingView; ++ public void setChunkTrackingView(ChunkTrackingView chunktrackingview) { ++ this.chunkTrackingView = chunktrackingview; + } + + @Override + public void playNotifySound(SoundEvent sound, SoundSource source, float volume, float pitch) { +- this.connection +- .send( +- new ClientboundSoundPacket( +- BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, this.getX(), this.getY(), this.getZ(), volume, pitch, this.random.nextLong() +- ) +- ); ++ this.connection.send(new ClientboundSoundPacket(BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, this.getX(), this.getY(), this.getZ(), volume, pitch, this.random.nextLong())); + } + + @Override + public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem) { +- ItemEntity itemEntity = super.drop(droppedItem, dropAround, traceItem); +- if (itemEntity == null) { ++ ItemEntity entityitem = super.drop(droppedItem, dropAround, traceItem); ++ ++ if (entityitem == null) { + return null; + } else { +- this.level().addFreshEntity(itemEntity); +- ItemStack item = itemEntity.getItem(); ++ this.level().addFreshEntity(entityitem); ++ ItemStack itemstack1 = entityitem.getItem(); ++ + if (traceItem) { +- if (!item.isEmpty()) { +- this.awardStat(Stats.ITEM_DROPPED.get(item.getItem()), droppedItem.getCount()); ++ if (!itemstack1.isEmpty()) { ++ this.awardStat(Stats.ITEM_DROPPED.get(itemstack1.getItem()), droppedItem.getCount()); + } + + this.awardStat(Stats.DROP); + } + +- return itemEntity; ++ return entityitem; + } + } + +@@ -1683,25 +2214,23 @@ + } + + private GameType calculateGameModeForNewPlayer(@Nullable GameType gameType) { +- GameType forcedGameType = this.server.getForcedGameType(); +- if (forcedGameType != null) { +- return forcedGameType; +- } else { +- return gameType != null ? gameType : this.server.getDefaultGameType(); +- } ++ GameType enumgamemode1 = this.server.getForcedGameType(); ++ ++ return enumgamemode1 != null ? enumgamemode1 : (gameType != null ? gameType : this.server.getDefaultGameType()); + } + + public void loadGameTypes(@Nullable CompoundTag tag) { +- this.gameMode +- .setGameModeForPlayer(this.calculateGameModeForNewPlayer(readPlayerMode(tag, "playerGameType")), readPlayerMode(tag, "previousPlayerGameType")); ++ this.gameMode.setGameModeForPlayer(this.calculateGameModeForNewPlayer(readPlayerMode(tag, "playerGameType")), readPlayerMode(tag, "previousPlayerGameType")); + } + + private void storeGameTypes(CompoundTag tag) { + tag.putInt("playerGameType", this.gameMode.getGameModeForPlayer().getId()); +- GameType previousGameModeForPlayer = this.gameMode.getPreviousGameModeForPlayer(); +- if (previousGameModeForPlayer != null) { +- tag.putInt("previousPlayerGameType", previousGameModeForPlayer.getId()); ++ GameType enumgamemode = this.gameMode.getPreviousGameModeForPlayer(); ++ ++ if (enumgamemode != null) { ++ tag.putInt("previousPlayerGameType", enumgamemode.getId()); + } ++ + } + + @Override +@@ -1710,7 +2239,7 @@ + } + + public boolean shouldFilterMessageTo(ServerPlayer player) { +- return player != this && (this.textFilteringEnabled || player.textFilteringEnabled); ++ return player == this ? false : this.textFilteringEnabled || player.textFilteringEnabled; + } + + @Override +@@ -1725,10 +2254,13 @@ + } + + public boolean drop(boolean dropStack) { +- Inventory inventory = this.getInventory(); +- ItemStack itemStack = inventory.removeFromSelected(dropStack); +- this.containerMenu.findSlot(inventory, inventory.selected).ifPresent(i -> this.containerMenu.setRemoteSlot(i, inventory.getSelected())); +- return this.drop(itemStack, false, true) != null; ++ Inventory playerinventory = this.getInventory(); ++ ItemStack itemstack = playerinventory.removeFromSelected(dropStack); ++ ++ this.containerMenu.findSlot(playerinventory, playerinventory.selected).ifPresent((i) -> { ++ this.containerMenu.setRemoteSlot(i, playerinventory.getSelected()); ++ }); ++ return this.drop(itemstack, false, true) != null; + } + + public boolean allowsListing() { +@@ -1743,10 +2275,12 @@ + @Override + public void onItemPickup(ItemEntity itemEntity) { + super.onItemPickup(itemEntity); +- Entity owner = itemEntity.getOwner(); +- if (owner != null) { +- CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_PLAYER.trigger(this, itemEntity.getItem(), owner); ++ Entity entity = itemEntity.getOwner(); ++ ++ if (entity != null) { ++ CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_PLAYER.trigger(this, itemEntity.getItem(), entity); + } ++ + } + + public void setChatSession(RemoteChatSession chatSession) { +@@ -1759,8 +2293,8 @@ + } + + @Override +- public void indicateDamage(double xDistance, double zDistance) { +- this.hurtDir = (float)(Mth.atan2(zDistance, xDistance) * 180.0F / (float)Math.PI - (double)this.getYRot()); ++ public void indicateDamage(double xDistance, double d1) { ++ this.hurtDir = (float) (Mth.atan2(d1, xDistance) * 57.2957763671875D - (double) this.getYRot()); + this.connection.send(new ClientboundHurtAnimationPacket(this)); + } + +@@ -1771,9 +2305,14 @@ + } else { + vehicle.positionRider(this); + this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); +- if (vehicle instanceof LivingEntity livingEntity) { +- for (MobEffectInstance mobEffectInstance : livingEntity.getActiveEffects()) { +- this.connection.send(new ClientboundUpdateMobEffectPacket(vehicle.getId(), mobEffectInstance)); ++ if (vehicle instanceof LivingEntity) { ++ LivingEntity entityliving = (LivingEntity) vehicle; ++ Iterator iterator = entityliving.getActiveEffects().iterator(); ++ ++ while (iterator.hasNext()) { ++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next(); ++ ++ this.connection.send(new ClientboundUpdateMobEffectPacket(vehicle.getId(), mobeffect)); + } + } + +@@ -1783,26 +2322,165 @@ + + @Override + public void stopRiding() { +- Entity vehicle = this.getVehicle(); ++ Entity entity = this.getVehicle(); ++ + super.stopRiding(); +- if (vehicle instanceof LivingEntity livingEntity) { +- for (MobEffectInstance mobEffectInstance : livingEntity.getActiveEffects()) { +- this.connection.send(new ClientboundRemoveMobEffectPacket(vehicle.getId(), mobEffectInstance.getEffect())); ++ if (entity instanceof LivingEntity) { ++ LivingEntity entityliving = (LivingEntity) entity; ++ Iterator iterator = entityliving.getActiveEffects().iterator(); ++ ++ while (iterator.hasNext()) { ++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next(); ++ ++ this.connection.send(new ClientboundRemoveMobEffectPacket(entity.getId(), mobeffect.getEffect())); + } + } ++ + } + +- public CommonPlayerSpawnInfo createCommonSpawnInfo(ServerLevel serverLevel) { +- return new CommonPlayerSpawnInfo( +- serverLevel.dimensionTypeId(), +- serverLevel.dimension(), +- BiomeManager.obfuscateSeed(serverLevel.getSeed()), +- this.gameMode.getGameModeForPlayer(), +- this.gameMode.getPreviousGameModeForPlayer(), +- serverLevel.isDebug(), +- serverLevel.isFlat(), +- this.getLastDeathLocation(), +- this.getPortalCooldown() +- ); ++ public CommonPlayerSpawnInfo createCommonSpawnInfo(ServerLevel worldserver) { ++ return new CommonPlayerSpawnInfo(worldserver.dimensionTypeId(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), this.gameMode.getGameModeForPlayer(), this.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), this.getLastDeathLocation(), this.getPortalCooldown()); + } ++ ++ // CraftBukkit start - Add per-player time and weather. ++ public long timeOffset = 0; ++ public boolean relativeTime = true; ++ ++ public long getPlayerTime() { ++ if (this.relativeTime) { ++ // Adds timeOffset to the current server time. ++ return this.level().getDayTime() + this.timeOffset; ++ } else { ++ // Adds timeOffset to the beginning of this day. ++ return this.level().getDayTime() - (this.level().getDayTime() % 24000) + this.timeOffset; ++ } ++ } ++ ++ public WeatherType weather = null; ++ ++ public WeatherType getPlayerWeather() { ++ return this.weather; ++ } ++ ++ public void setPlayerWeather(WeatherType type, boolean plugin) { ++ if (!plugin && this.weather != null) { ++ return; ++ } ++ ++ if (plugin) { ++ this.weather = type; ++ } ++ ++ if (type == WeatherType.DOWNFALL) { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0)); ++ } else { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0)); ++ } ++ } ++ ++ private float pluginRainPosition; ++ private float pluginRainPositionPrevious; ++ ++ public void updateWeather(float oldRain, float newRain, float oldThunder, float newThunder) { ++ if (this.weather == null) { ++ // Vanilla ++ if (oldRain != newRain) { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, newRain)); ++ } ++ } else { ++ // Plugin ++ if (pluginRainPositionPrevious != pluginRainPosition) { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, pluginRainPosition)); ++ } ++ } ++ ++ if (oldThunder != newThunder) { ++ if (weather == WeatherType.DOWNFALL || weather == null) { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, newThunder)); ++ } else { ++ this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, 0)); ++ } ++ } ++ } ++ ++ public void tickWeather() { ++ if (this.weather == null) return; ++ ++ pluginRainPositionPrevious = pluginRainPosition; ++ if (weather == WeatherType.DOWNFALL) { ++ pluginRainPosition += 0.01; ++ } else { ++ pluginRainPosition -= 0.01; ++ } ++ ++ pluginRainPosition = Mth.clamp(pluginRainPosition, 0.0F, 1.0F); ++ } ++ ++ public void resetPlayerWeather() { ++ this.weather = null; ++ this.setPlayerWeather(this.level().getLevelData().isRaining() ? WeatherType.DOWNFALL : WeatherType.CLEAR, false); ++ } ++ ++ @Override ++ public String toString() { ++ return super.toString() + "(" + this.getScoreboardName() + " at " + this.getX() + "," + this.getY() + "," + this.getZ() + ")"; ++ } ++ ++ // SPIGOT-1903, MC-98153 ++ public void forceSetPositionRotation(double x, double y, double z, float yaw, float pitch) { ++ this.moveTo(x, y, z, yaw, pitch); ++ this.connection.resetPosition(); ++ } ++ ++ @Override ++ public boolean isImmobile() { ++ return super.isImmobile() || !getBukkitEntity().isOnline(); ++ } ++ ++ @Override ++ public Scoreboard getScoreboard() { ++ return getBukkitEntity().getScoreboard().getHandle(); ++ } ++ ++ public void reset() { ++ float exp = 0; ++ boolean keepInventory = this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY); ++ ++ if (this.keepLevel) { // CraftBukkit - SPIGOT-6687: Only use keepLevel (was pre-set with RULE_KEEPINVENTORY value in PlayerDeathEvent) ++ exp = this.experienceProgress; ++ this.newTotalExp = this.totalExperience; ++ this.newLevel = this.experienceLevel; ++ } ++ ++ this.setHealth(this.getMaxHealth()); ++ this.stopUsingItem(); // CraftBukkit - SPIGOT-6682: Clear active item on reset ++ this.setRemainingFireTicks(0); ++ this.fallDistance = 0; ++ this.foodData = new FoodData(this); ++ this.experienceLevel = this.newLevel; ++ this.totalExperience = this.newTotalExp; ++ this.experienceProgress = 0; ++ this.deathTime = 0; ++ this.setArrowCount(0, true); // CraftBukkit - ArrowBodyCountChangeEvent ++ this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DEATH); ++ this.effectsDirty = true; ++ this.containerMenu = this.inventoryMenu; ++ this.lastHurtByPlayer = null; ++ this.lastHurtByMob = null; ++ this.combatTracker = new CombatTracker(this); ++ this.lastSentExp = -1; ++ if (this.keepLevel) { // CraftBukkit - SPIGOT-6687: Only use keepLevel (was pre-set with RULE_KEEPINVENTORY value in PlayerDeathEvent) ++ this.experienceProgress = exp; ++ } else { ++ this.giveExperiencePoints(this.newExp); ++ } ++ this.keepLevel = false; ++ this.setDeltaMovement(0, 0, 0); // CraftBukkit - SPIGOT-6948: Reset velocity on death ++ } ++ ++ @Override ++ public CraftPlayer getBukkitEntity() { ++ return (CraftPlayer) super.getBukkitEntity(); ++ } ++ // CraftBukkit end + } |