From 18f38c316700dbc231e6846e5fa82d6d47c4244f Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 27 Mar 2019 23:01:33 -0400 Subject: [PATCH] PlayerDeathEvent#getItemsToKeep Exposes a mutable array on items a player should keep on death Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4 == AT == public net.minecraft.world.entity.player.Inventory compartments --- .../server/level/ServerPlayer.java.patch | 196 +++++++++++------- 1 file changed, 118 insertions(+), 78 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index 237e8e9f59..bf97e6ff19 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -114,7 +114,7 @@ @Nullable private Vec3 startingToFallPosition; @Nullable -@@ -258,7 +293,32 @@ +@@ -258,6 +293,31 @@ private final CommandSource commandSource; private int containerCounter; public boolean wonGame; @@ -124,7 +124,7 @@ + public boolean queueHealthUpdatePacket; + public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket; + // Paper end - cancellable death event - ++ + // CraftBukkit start + public CraftPlayer.TransferCookieConnection transferCookieConnection; + public String displayName; @@ -143,10 +143,9 @@ + // CraftBukkit end + public boolean isRealPlayer; // Paper + public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent -+ + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientOptions) { super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); - this.chatVisibility = ChatVisiblity.FULL; @@ -266,7 +326,7 @@ this.canChatColor = true; this.lastActionTime = Util.getMillis(); @@ -180,8 +179,8 @@ + this.adventure$displayName = net.kyori.adventure.text.Component.text(this.getScoreboardName()); // Paper + this.bukkitPickUpLoot = true; + this.maxHealthCache = this.getMaxHealth(); -+ } -+ + } + + // Use method to resend items in hands in case of client desync, because the item use got cancelled. + // For example, when cancelling the leash event + public void resendItemInHands() { @@ -227,9 +226,9 @@ + } + + return blockposition; - } ++ } + // CraftBukkit end - ++ @Override public BlockPos adjustSpawnLocation(ServerLevel world, BlockPos basePos) { AABB axisalignedbb = this.getDimensions(Pose.STANDING).makeBoundingBox(Vec3.ZERO); @@ -280,16 +279,16 @@ if (this.isSleeping()) { this.stopSleeping(); - } - ++ } ++ + // CraftBukkit start + String spawnWorld = nbt.getString("SpawnWorld"); + CraftWorld oldWorld = (CraftWorld) Bukkit.getWorld(spawnWorld); + if (oldWorld != null) { + this.respawnDimension = oldWorld.getHandle().dimension(); -+ } + } + // CraftBukkit end -+ + if (nbt.contains("SpawnX", 99) && nbt.contains("SpawnY", 99) && nbt.contains("SpawnZ", 99)) { this.respawnPosition = new BlockPos(nbt.getInt("SpawnX"), nbt.getInt("SpawnY"), nbt.getInt("SpawnZ")); this.respawnForced = nbt.getBoolean("SpawnForced"); @@ -390,12 +389,10 @@ Logger logger = ServerPlayer.LOGGER; Objects.requireNonNull(logger); -@@ -684,7 +834,30 @@ - } - } +@@ -686,6 +836,29 @@ + + } -+ } -+ + // CraftBukkit start - World fallback code, either respawn location or global spawn + public void spawnIn(Level world) { + this.setLevel(world); @@ -416,11 +413,12 @@ + this.setPos(position); + } + this.gameMode.setLevel((ServerLevel) world); - } ++ } + // CraftBukkit end - ++ public void setExperiencePoints(int points) { float f = (float) this.getXpNeededForNextLevel(); + float f1 = (f - 1.0F) / f; @@ -744,6 +917,11 @@ @Override @@ -471,17 +469,15 @@ if (this.experienceLevel != this.lastRecordedLevel) { this.lastRecordedLevel = this.experienceLevel; this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil((float) this.lastRecordedLevel)); -@@ -863,8 +1051,22 @@ - - if (this.tickCount % 20 == 0) { +@@ -865,6 +1053,20 @@ CriteriaTriggers.LOCATION.trigger(this); -+ } -+ + } + + // 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; @@ -503,7 +499,7 @@ } float f = this.foodData.getSaturationLevel(); -@@ -946,19 +1148,63 @@ +@@ -946,19 +1148,103 @@ } private void updateScoreForCriteria(ObjectiveCriteria criterion, int score) { @@ -514,6 +510,46 @@ }); } ++ // Paper start - PlayerDeathEvent#getItemsToKeep ++ private static void processKeep(org.bukkit.event.entity.PlayerDeathEvent event, NonNullList inv) { ++ List itemsToKeep = event.getItemsToKeep(); ++ if (inv == null) { ++ // remainder of items left in toKeep - plugin added stuff on death that wasn't in the initial loot? ++ if (!itemsToKeep.isEmpty()) { ++ for (org.bukkit.inventory.ItemStack itemStack : itemsToKeep) { ++ event.getEntity().getInventory().addItem(itemStack); ++ } ++ } ++ ++ return; ++ } ++ ++ for (int i = 0; i < inv.size(); ++i) { ++ ItemStack item = inv.get(i); ++ if (EnchantmentHelper.has(item, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP) || itemsToKeep.isEmpty() || item.isEmpty()) { ++ inv.set(i, ItemStack.EMPTY); ++ continue; ++ } ++ ++ final org.bukkit.inventory.ItemStack bukkitStack = item.getBukkitStack(); ++ boolean keep = false; ++ final Iterator iterator = itemsToKeep.iterator(); ++ while (iterator.hasNext()) { ++ final org.bukkit.inventory.ItemStack itemStack = iterator.next(); ++ if (bukkitStack.equals(itemStack)) { ++ iterator.remove(); ++ keep = true; ++ break; ++ } ++ } ++ ++ if (!keep) { ++ inv.set(i, ItemStack.EMPTY); ++ } ++ } ++ } ++ // Paper end - PlayerDeathEvent#getItemsToKeep ++ @Override public void die(DamageSource damageSource) { - this.gameEvent(GameEvent.ENTITY_DIE); @@ -538,7 +574,7 @@ + // SPIGOT-5071: manually add player loot tables (SPIGOT-5195 - ignores keepInventory rule) + this.dropFromLootTable(this.serverLevel(), damageSource, this.lastHurtByPlayerTime > 0); + this.dropCustomDeathLoot(this.serverLevel(), damageSource, flag); -+ + + loot.addAll(this.drops); + this.drops.clear(); // SPIGOT-5188: make sure to clear + @@ -564,14 +600,14 @@ + } + + net.kyori.adventure.text.Component deathMessage = event.deathMessage() != null ? event.deathMessage() : net.kyori.adventure.text.Component.empty(); // Paper - Adventure - ++ + if (deathMessage != null && deathMessage != net.kyori.adventure.text.Component.empty() && flag) { // Paper - Adventure // TODO: allow plugins to override? + Component ichatbasecomponent = PaperAdventure.asVanilla(deathMessage); // Paper - Adventure + this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), ichatbasecomponent), PacketSendListener.exceptionallySend(() -> { boolean flag1 = true; String s = ichatbasecomponent.getString(256); -@@ -988,12 +1234,18 @@ +@@ -988,12 +1274,23 @@ if (this.serverLevel().getGameRules().getBoolean(GameRules.RULE_FORGIVE_DEAD_PLAYERS)) { this.tellNeutralMobsThatIDied(); } @@ -582,7 +618,12 @@ + this.dropExperience(this.serverLevel(), damageSource.getEntity()); + // 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(); ++ // Paper start - PlayerDeathEvent#getItemsToKeep ++ for (NonNullList inv : this.getInventory().compartments) { ++ processKeep(event, inv); ++ } ++ processKeep(event, null); ++ // Paper end - PlayerDeathEvent#getItemsToKeep } - this.getScoreboard().forAllObjectives(ObjectiveCriteria.DEATH_COUNT, this, ScoreAccess::increment); @@ -594,7 +635,7 @@ LivingEntity entityliving = this.getKillCredit(); if (entityliving != null) { -@@ -1028,10 +1280,12 @@ +@@ -1028,10 +1325,12 @@ public void awardKillScore(Entity entityKilled, DamageSource damageSource) { if (entityKilled != this) { super.awardKillScore(entityKilled, damageSource); @@ -610,7 +651,7 @@ } else { this.awardStat(Stats.MOB_KILLS); } -@@ -1049,7 +1303,8 @@ +@@ -1049,7 +1348,8 @@ int i = scoreboardteam.getColor().getId(); if (i >= 0 && i < criterions.length) { @@ -620,7 +661,7 @@ } } -@@ -1062,8 +1317,8 @@ +@@ -1062,8 +1362,8 @@ } else { Entity entity = source.getEntity(); @@ -631,7 +672,7 @@ if (!this.canHarmPlayer(entityhuman)) { return false; -@@ -1074,8 +1329,8 @@ +@@ -1074,8 +1374,8 @@ AbstractArrow entityarrow = (AbstractArrow) entity; Entity entity1 = entityarrow.getOwner(); @@ -642,7 +683,7 @@ if (!this.canHarmPlayer(entityhuman1)) { return false; -@@ -1083,38 +1338,78 @@ +@@ -1083,38 +1383,78 @@ } } @@ -729,7 +770,7 @@ } public static Optional findRespawnAndUseSpawnBlock(ServerLevel world, BlockPos pos, float spawnAngle, boolean spawnForced, boolean alive) { -@@ -1129,11 +1424,11 @@ +@@ -1129,11 +1469,11 @@ } return optional.map((vec3d) -> { @@ -743,7 +784,7 @@ }); } else if (!spawnForced) { return Optional.empty(); -@@ -1142,7 +1437,7 @@ +@@ -1142,7 +1482,7 @@ BlockState iblockdata1 = world.getBlockState(pos.above()); boolean flag3 = iblockdata1.getBlock().isPossibleToRespawnInThis(iblockdata1); @@ -752,7 +793,7 @@ } } -@@ -1160,6 +1455,7 @@ +@@ -1160,6 +1500,7 @@ @Nullable @Override public ServerPlayer teleport(TeleportTransition teleportTarget) { @@ -760,7 +801,7 @@ if (this.isRemoved()) { return null; } else { -@@ -1169,39 +1465,78 @@ +@@ -1169,39 +1510,78 @@ ServerLevel worldserver = teleportTarget.newLevel(); ServerLevel worldserver1 = this.serverLevel(); @@ -847,7 +888,7 @@ this.connection.resetPosition(); worldserver.addDuringTeleport(this); gameprofilerfiller.pop(); -@@ -1215,12 +1550,30 @@ +@@ -1215,12 +1595,30 @@ this.lastSentExp = -1; this.lastSentHealth = -1.0F; this.lastSentFood = -1; @@ -878,7 +919,7 @@ public void forceSetRotation(float yaw, float pitch) { this.connection.send(new ClientboundPlayerRotationPacket(yaw, pitch)); } -@@ -1228,13 +1581,21 @@ +@@ -1228,13 +1626,21 @@ public void triggerDimensionChangeTriggers(ServerLevel origin) { ResourceKey resourcekey = origin.dimension(); ResourceKey resourcekey1 = this.level().dimension(); @@ -903,7 +944,7 @@ this.enteredNetherPosition = null; } -@@ -1251,36 +1612,63 @@ +@@ -1251,36 +1657,63 @@ this.containerMenu.broadcastChanges(); } @@ -982,7 +1023,7 @@ this.awardStat(Stats.SLEEP_IN_BED); CriteriaTriggers.SLEPT_IN_BED.trigger(this); }); -@@ -1293,9 +1681,8 @@ +@@ -1293,9 +1726,8 @@ return either; } } @@ -993,7 +1034,7 @@ } @Override -@@ -1322,13 +1709,31 @@ +@@ -1322,13 +1754,31 @@ @Override public void stopSleepInBed(boolean skipSleepTimer, boolean updateSleepingPlayers) { @@ -1026,7 +1067,7 @@ } } -@@ -1387,8 +1792,9 @@ +@@ -1387,8 +1837,9 @@ this.connection.send(new ClientboundOpenSignEditorPacket(sign.getBlockPos(), front)); } @@ -1037,7 +1078,7 @@ } @Override -@@ -1396,13 +1802,35 @@ +@@ -1396,13 +1847,35 @@ if (factory == null) { return OptionalInt.empty(); } else { @@ -1051,11 +1092,11 @@ this.nextContainerCounter(); AbstractContainerMenu container = factory.createMenu(this.containerCounter, this.getInventory(), this); - ++ + // CraftBukkit start - Inventory open hook + if (container != null) { + container.setTitle(factory.getDisplayName()); -+ + + boolean cancelled = false; + container = CraftEventFactory.callInventoryOpenEvent(this, container, cancelled); + if (container == null && !cancelled) { // Let pre-cancelled events fall through @@ -1073,7 +1114,7 @@ if (container == null) { if (this.isSpectator()) { this.displayClientMessage(Component.translatable("container.spectatorCantOpen").withStyle(ChatFormatting.RED), true); -@@ -1410,9 +1838,11 @@ +@@ -1410,9 +1883,11 @@ return OptionalInt.empty(); } else { @@ -1087,7 +1128,7 @@ return OptionalInt.of(this.containerCounter); } } -@@ -1425,15 +1855,26 @@ +@@ -1425,15 +1900,26 @@ @Override public void openHorseInventory(AbstractHorse horse, Container inventory) { @@ -1117,7 +1158,7 @@ this.initMenu(this.containerMenu); } -@@ -1456,6 +1897,13 @@ +@@ -1456,6 +1942,13 @@ @Override public void closeContainer() { @@ -1131,7 +1172,7 @@ this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); this.doCloseContainer(); } -@@ -1485,19 +1933,19 @@ +@@ -1485,19 +1978,19 @@ i = Math.round((float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) * 100.0F); if (i > 0) { this.awardStat(Stats.SWIM_ONE_CM, i); @@ -1154,7 +1195,7 @@ } } else if (this.onClimbable()) { if (deltaY > 0.0D) { -@@ -1508,13 +1956,13 @@ +@@ -1508,13 +2001,13 @@ if (i > 0) { if (this.isSprinting()) { this.awardStat(Stats.SPRINT_ONE_CM, i); @@ -1171,7 +1212,7 @@ } } } else if (this.isFallFlying()) { -@@ -1557,7 +2005,7 @@ +@@ -1557,7 +2050,7 @@ @Override public void awardStat(Stat stat, int amount) { this.stats.increment(this, stat, amount); @@ -1180,7 +1221,7 @@ scoreaccess.add(amount); }); } -@@ -1565,7 +2013,7 @@ +@@ -1565,7 +2058,7 @@ @Override public void resetStat(Stat stat) { this.stats.setValue(this, stat, 0); @@ -1189,7 +1230,7 @@ } @Override -@@ -1597,9 +2045,9 @@ +@@ -1597,9 +2090,9 @@ super.jumpFromGround(); this.awardStat(Stats.JUMP); if (this.isSprinting()) { @@ -1201,7 +1242,7 @@ } } -@@ -1613,6 +2061,13 @@ +@@ -1613,6 +2106,13 @@ public void disconnect() { this.disconnected = true; this.ejectPassengers(); @@ -1215,7 +1256,7 @@ if (this.isSleeping()) { this.stopSleepInBed(true, false); } -@@ -1625,6 +2080,7 @@ +@@ -1625,6 +2125,7 @@ public void resetSentInfo() { this.lastSentHealth = -1.0E8F; @@ -1223,7 +1264,7 @@ } @Override -@@ -1661,7 +2117,7 @@ +@@ -1661,7 +2162,7 @@ this.onUpdateAbilities(); if (alive) { this.getAttributes().assignBaseValues(oldPlayer.getAttributes()); @@ -1232,7 +1273,7 @@ this.setHealth(oldPlayer.getHealth()); this.foodData = oldPlayer.foodData; Iterator iterator = oldPlayer.getActiveEffects().iterator(); -@@ -1669,7 +2125,7 @@ +@@ -1669,7 +2170,7 @@ while (iterator.hasNext()) { MobEffectInstance mobeffect = (MobEffectInstance) iterator.next(); @@ -1241,7 +1282,7 @@ } this.getInventory().replaceWith(oldPlayer.getInventory()); -@@ -1680,7 +2136,7 @@ +@@ -1680,7 +2181,7 @@ this.portalProcess = oldPlayer.portalProcess; } else { this.getAttributes().assignBaseValues(oldPlayer.getAttributes()); @@ -1250,7 +1291,7 @@ if (this.serverLevel().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || oldPlayer.isSpectator()) { this.getInventory().replaceWith(oldPlayer.getInventory()); this.experienceLevel = oldPlayer.experienceLevel; -@@ -1696,7 +2152,7 @@ +@@ -1696,7 +2197,7 @@ this.lastSentExp = -1; this.lastSentHealth = -1.0F; this.lastSentFood = -1; @@ -1259,7 +1300,7 @@ this.seenCredits = oldPlayer.seenCredits; this.enteredNetherPosition = oldPlayer.enteredNetherPosition; this.chunkTrackingView = oldPlayer.chunkTrackingView; -@@ -1752,19 +2208,19 @@ +@@ -1752,19 +2253,19 @@ } @Override @@ -1283,7 +1324,7 @@ } return flag1; -@@ -1861,8 +2317,13 @@ +@@ -1861,8 +2362,13 @@ } public void sendChatMessage(OutgoingChatMessage message, boolean filterMaskEnabled, ChatType.Bound params) { @@ -1298,7 +1339,7 @@ } } -@@ -1878,7 +2339,18 @@ +@@ -1878,7 +2384,18 @@ } public void updateOptions(ClientInformation clientOptions) { @@ -1317,7 +1358,7 @@ this.requestedViewDistance = clientOptions.viewDistance(); this.chatVisibility = clientOptions.chatVisibility(); this.canChatColor = clientOptions.chatColors(); -@@ -1957,12 +2429,27 @@ +@@ -1957,12 +2474,27 @@ this.camera = (Entity) (entity == null ? this : entity); if (entity1 != this.camera) { @@ -1346,7 +1387,7 @@ } if (entity != null) { -@@ -1999,11 +2486,11 @@ +@@ -1999,11 +2531,11 @@ @Nullable public Component getTabListDisplayName() { @@ -1360,7 +1401,7 @@ } @Override -@@ -2046,17 +2533,43 @@ +@@ -2046,17 +2578,43 @@ } public void setRespawnPosition(ResourceKey dimension, @Nullable BlockPos pos, float angle, boolean forced, boolean sendMessage) { @@ -1411,7 +1452,7 @@ } else { this.respawnPosition = null; this.respawnDimension = Level.OVERWORLD; -@@ -2088,18 +2601,44 @@ +@@ -2088,18 +2646,44 @@ } @Override @@ -1460,7 +1501,7 @@ } this.awardStat(Stats.DROP); -@@ -2275,9 +2814,15 @@ +@@ -2275,9 +2859,15 @@ @Override public void stopRiding() { @@ -1477,7 +1518,7 @@ if (entity instanceof LivingEntity entityliving) { Iterator iterator = entityliving.getActiveEffects().iterator(); -@@ -2375,16 +2920,161 @@ +@@ -2375,10 +2965,12 @@ return TicketType.ENDER_PEARL.timeout(); } @@ -1493,11 +1534,10 @@ } private static float calculateLookAtYaw(Vec3 respawnPos, BlockPos currentPos) { - Vec3 vec3d1 = Vec3.atBottomCenterOf(currentPos).subtract(respawnPos).normalize(); - +@@ -2387,4 +2979,147 @@ return (float) Mth.wrapDegrees(Mth.atan2(vec3d1.z, vec3d1.x) * 57.2957763671875D - 90.0D); -+ } -+ } + } + } + + // CraftBukkit start - Add per-player time and weather. + public long timeOffset = 0; @@ -1510,8 +1550,8 @@ + } else { + // Adds timeOffset to the beginning of this day. + return this.level().getDayTime() - (this.level().getDayTime() % 24000) + this.timeOffset; - } - } ++ } ++ } + + public WeatherType weather = null; +