--- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -152,7 +162,29 @@ import net.minecraft.world.scores.ScoreHolder; 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(); @@ -187,8 +220,8 @@ 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 @@ -212,8 +246,41 @@ 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]); + // 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 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]); + } + } } @@ -247,8 +315,57 @@ } } - @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 @@ -267,14 +370,17 @@ this.fudgeSpawnLocation(level); this.updateOptions(clientInformation); } + // 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) { @@ -333,11 +443,20 @@ 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"); @@ -368,17 +493,32 @@ compound.put("enteredNetherPosition", compoundTag); } - 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) { @@ -392,8 +534,33 @@ .resultOrPartial(LOGGER::error) .ifPresent(tag -> compound.put("SpawnDimension", tag)); } + 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 f1 = (f - 1.0F) / f; @@ -451,6 +619,11 @@ @Override public void tick() { + // CraftBukkit start + if (this.joining) { + this.joining = false; + } + // CraftBukkit end this.gameMode.tick(); this.wardenSpawnTracker.tick(); this.spawnInvulnerableTime--; @@ -503,10 +679,8 @@ } } - 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; @@ -537,6 +711,12 @@ 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)); @@ -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); } } @@ -589,38 +787,28 @@ } 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 loot = new java.util.ArrayList(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)); + } } } else { this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), CommonComponents.EMPTY)); @@ -635,12 +875,17 @@ this.dropAllDeathLoot(cause); } - 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); @@ -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,12 +930,15 @@ } } - 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); } } } @@ -721,16 +988,20 @@ } 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; } @@ -739,10 +1010,20 @@ @Nullable @Override public Entity changeDimension(ServerLevel server) { - this.isChangingDimension = true; - ServerLevel serverLevel = this.serverLevel(); - ResourceKey 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 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,26 +1034,61 @@ 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(); @@ -789,50 +1108,90 @@ 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) (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 getExitPortal(ServerLevel destination, BlockPos findFrom, boolean isToNether, WorldBorder worldBorder) { - Optional optional = super.getExitPortal(destination, findFrom, isToNether, worldBorder); - if (optional.isPresent()) { + protected Optional getExitPortal(ServerLevel worldserver, BlockPos blockposition, boolean flag, WorldBorder worldborder, int searchRadius, boolean canCreatePortal, int createRadius) { // CraftBukkit + Optional 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 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 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 resourceKey = level.dimension(); - ResourceKey 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 resourcekey = level.dimension(); + ResourceKey resourcekey1 = this.level().dimension(); + // CraftBukkit start + ResourceKey maindimensionkey = CraftDimensionUtil.getMainDimensionKey(level); + ResourceKey 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; } } @@ -848,34 +1208,31 @@ this.containerMenu.broadcastChanges(); } - @Override - public Either 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 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 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 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); + } } } @@ -891,6 +1278,7 @@ return either; } } + // CraftBukkit end } @Override @@ -915,13 +1305,31 @@ @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 } } @@ -968,8 +1379,9 @@ 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 @@ -977,23 +1389,47 @@ 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,13 +1442,24 @@ @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); } @@ -1034,6 +1482,7 @@ @Override public void closeContainer() { + CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); this.doCloseContainer(); } @@ -1056,6 +1505,16 @@ } 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); } } @@ -1081,22 +1545,22 @@ public void checkMovementStatistics(double d, double d1, double d2) { if (!this.isPassenger() && !didNotMove(d, d1, d2)) { 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) { @@ -1106,14 +1570,14 @@ int rounded = Math.round((float)Math.sqrt(d * d + d2 * d2) * 100.0F); if (rounded > 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()) { @@ -1159,7 +1628,7 @@ @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 @@ -1205,6 +1676,7 @@ public void resetSentInfo() { this.lastSentHealth = -1.0E8F; + this.lastSentExp = -1; // CraftBukkit - Added to reset } @Override @@ -1260,7 +1734,7 @@ 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; @@ -1310,18 +1784,25 @@ } @Override - public boolean teleportTo(ServerLevel level, double x, double y, double z, Set 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 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 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); @@ -1424,15 +1905,25 @@ : ""; } - 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() { @@ -1504,11 +1990,16 @@ } 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) { @@ -1542,7 +2036,7 @@ @Nullable public Component getTabListDisplayName() { - return null; + return listName; // CraftBukkit } @Override @@ -1563,11 +2057,18 @@ 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(); @@ -1584,6 +2086,10 @@ this.server.getPlayerList().sendLevelInfo(this, newLevel); this.server.getPlayerList().sendAllPlayerInfo(this); } + */ + this.getBukkitEntity().teleport(new Location(worldserver.getWorld(), d0, d1, d2, f, f1), cause); + // CraftBukkit end + } @Nullable @@ -1604,9 +2110,36 @@ } public void setRespawnPosition(ResourceKey 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 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")); } @@ -1805,4 +2341,146 @@ 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 }