Skip Improve-exact-choice-recipe-ingredients for now

This commit is contained in:
Bjarne Koll 2024-10-23 14:12:17 +02:00
parent 8851d25a4d
commit f677393a88
No known key found for this signature in database
GPG key ID: 27F6CCCF55D2EE62
27 changed files with 129 additions and 158 deletions

View file

@ -1,27 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Phoenix616 <mail@moep.tv>
Date: Tue, 18 Sep 2018 23:53:23 +0100
Subject: [PATCH] PreSpawnerSpawnEvent
This adds a separate event before an entity is spawned by a spawner
which contains the location of the spawner too similarly to how the
SpawnerSpawnEvent gets called instead of the CreatureSpawnEvent for
spawners.
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
index d13abdcc7a54bdecf853c883911ef535733610b4..ee897b8c9462dbb3d7be9a2994753155065ce205 100644
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
@@ -133,10 +133,10 @@ public abstract class BaseSpawner {
continue;
}
// Paper start - PreCreatureSpawnEvent
- com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+ com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent(
io.papermc.paper.util.MCUtil.toLocation(world, d0, d1, d2),
org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(optional.get()),
- org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER
+ io.papermc.paper.util.MCUtil.toLocation(world, pos)
);
if (!event.callEvent()) {
flag = true;

View file

@ -1,108 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sat, 22 Sep 2018 00:33:08 -0500
Subject: [PATCH] Add LivingEntity#getTargetEntity
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 64aa52c2d1fe50d304d75ebb197e8a834016c3cf..7bc0a66602d77902d83d6ca515da48e3453de900 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -127,6 +127,7 @@ import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.AABB;
+import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.PlayerTeam;
@@ -4044,6 +4045,38 @@ public abstract class LivingEntity extends Entity implements Attackable {
return this.level().clip(raytrace);
}
+ public @Nullable EntityHitResult getTargetEntity(int maxDistance) {
+ if (maxDistance < 1 || maxDistance > 120) {
+ throw new IllegalArgumentException("maxDistance must be between 1-120");
+ }
+
+ Vec3 start = this.getEyePosition(1.0F);
+ Vec3 direction = this.getLookAngle();
+ Vec3 end = start.add(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance);
+
+ List<Entity> entityList = this.level().getEntities(this, getBoundingBox().expandTowards(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).inflate(1.0D, 1.0D, 1.0D), EntitySelector.NO_SPECTATORS.and(Entity::isPickable));
+
+ double distance = 0.0D;
+ EntityHitResult result = null;
+
+ for (Entity entity : entityList) {
+ final double inflationAmount = (double) entity.getPickRadius();
+ AABB aabb = entity.getBoundingBox().inflate(inflationAmount, inflationAmount, inflationAmount);
+ Optional<Vec3> rayTraceResult = aabb.clip(start, end);
+
+ if (rayTraceResult.isPresent()) {
+ Vec3 rayTrace = rayTraceResult.get();
+ double distanceTo = start.distanceToSqr(rayTrace);
+ if (distanceTo < distance || distance == 0.0D) {
+ result = new EntityHitResult(entity, rayTrace);
+ distance = distanceTo;
+ }
+ }
+ }
+
+ return result;
+ }
+
public int shieldBlockingDelay = this.level().paperConfig().misc.shieldBlockingDelay;
public int getShieldBlockingDelay() {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index c8ac50351b7b1b2f4afc138570b8098a3c0ce1ba..c0684f1864ece26b4f337ac615db04f615957c13 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -1,5 +1,6 @@
package org.bukkit.craftbukkit.entity;
+import com.destroystokyo.paper.entity.TargetEntityInfo;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -227,6 +228,39 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
}
return null;
}
+
+ public Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) {
+ net.minecraft.world.phys.EntityHitResult rayTrace = rayTraceEntity(maxDistance, ignoreBlocks);
+ return rayTrace == null ? null : rayTrace.getEntity().getBukkitEntity();
+ }
+
+ public TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) {
+ net.minecraft.world.phys.EntityHitResult rayTrace = rayTraceEntity(maxDistance, ignoreBlocks);
+ return rayTrace == null ? null : new TargetEntityInfo(rayTrace.getEntity().getBukkitEntity(), new org.bukkit.util.Vector(rayTrace.getLocation().x, rayTrace.getLocation().y, rayTrace.getLocation().z));
+ }
+
+ @Override
+ public RayTraceResult rayTraceEntities(int maxDistance, boolean ignoreBlocks) {
+ net.minecraft.world.phys.EntityHitResult rayTrace = this.rayTraceEntity(maxDistance, ignoreBlocks);
+ return rayTrace == null ? null : new org.bukkit.util.RayTraceResult(org.bukkit.craftbukkit.util.CraftVector.toBukkit(rayTrace.getLocation()), rayTrace.getEntity().getBukkitEntity());
+ }
+
+ public net.minecraft.world.phys.EntityHitResult rayTraceEntity(int maxDistance, boolean ignoreBlocks) {
+ net.minecraft.world.phys.EntityHitResult rayTrace = getHandle().getTargetEntity(maxDistance);
+ if (rayTrace == null) {
+ return null;
+ }
+ if (!ignoreBlocks) {
+ net.minecraft.world.phys.HitResult rayTraceBlocks = getHandle().getRayTrace(maxDistance, net.minecraft.world.level.ClipContext.Fluid.NONE);
+ if (rayTraceBlocks != null) {
+ net.minecraft.world.phys.Vec3 eye = getHandle().getEyePosition(1.0F);
+ if (eye.distanceToSqr(rayTraceBlocks.getLocation()) <= eye.distanceToSqr(rayTrace.getLocation())) {
+ return null;
+ }
+ }
+ }
+ return rayTrace;
+ }
// Paper end
@Override

View file

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sun, 7 Oct 2018 00:54:21 -0500
Subject: [PATCH] Add sun related API
== AT ==
public net.minecraft.world.entity.Mob isSunBurnTick()Z
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 333e3109d19c867e8a74f20693952bdb3df804e4..20bb365b188c7081123db87186f0e1a999758817 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -756,6 +756,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
}
+ // Paper start
+ @Override
+ public boolean isDayTime() {
+ return getHandle().isDay();
+ }
+ // Paper end
+
@Override
public long getGameTime() {
return this.world.levelData.getGameTime();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
index d597eea5d5c2f223e87bff06f292619657596f1f..2a8596e4f9d7be966c18e867c2c7b5bfbea9742c 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
@@ -90,4 +90,11 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob {
public long getSeed() {
return this.getHandle().lootTableSeed;
}
+
+ // Paper start
+ @Override
+ public boolean isInDaylight() {
+ return getHandle().isSunBurnTick();
+ }
+ // Paper end
}

View file

@ -1,83 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sat, 29 Sep 2018 16:08:23 -0500
Subject: [PATCH] Turtle API
== AT ==
public net.minecraft.world.entity.animal.Turtle getHomePos()Lnet/minecraft/core/BlockPos;
public net.minecraft.world.entity.animal.Turtle setHasEgg(Z)V
public net.minecraft.world.entity.animal.Turtle isGoingHome()Z
public net.minecraft.world.entity.animal.Turtle setGoingHome(Z)V
public net.minecraft.world.entity.animal.Turtle isTravelling()Z
public net.minecraft.world.entity.animal.Turtle setTravelling(Z)V
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
index 6f90ee749aed98b97868aa40fc233d164ddc2ef6..34e6bf677a9f4548f3febe6d57e6af9602530271 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
@@ -487,14 +487,17 @@ public class Turtle extends Animal {
if (!this.turtle.isInWater() && this.isReachedTarget()) {
if (this.turtle.layEggCounter < 1) {
- this.turtle.setLayingEgg(true);
+ this.turtle.setLayingEgg(new com.destroystokyo.paper.event.entity.TurtleStartDiggingEvent((org.bukkit.entity.Turtle) this.turtle.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(this.turtle.level(), this.blockPos)).callEvent()); // Paper - Turtle API
} else if (this.turtle.layEggCounter > this.adjustedTickDelay(200)) {
Level world = this.turtle.level();
- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.turtle, this.blockPos.above(), (BlockState) Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, this.turtle.random.nextInt(4) + 1))) { // CraftBukkit
+ // Paper start - Turtle API
+ int eggCount = this.turtle.random.nextInt(4) + 1;
+ com.destroystokyo.paper.event.entity.TurtleLayEggEvent layEggEvent = new com.destroystokyo.paper.event.entity.TurtleLayEggEvent((org.bukkit.entity.Turtle) this.turtle.getBukkitEntity(), io.papermc.paper.util.MCUtil.toLocation(this.turtle.level(), this.blockPos.above()), eggCount);
+ if (layEggEvent.callEvent() && org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.turtle, this.blockPos.above(), Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, layEggEvent.getEggCount()))) {
world.playSound((Player) null, blockposition, SoundEvents.TURTLE_LAY_EGG, SoundSource.BLOCKS, 0.3F, 0.9F + world.random.nextFloat() * 0.2F);
BlockPos blockposition1 = this.blockPos.above();
- BlockState iblockdata = (BlockState) Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, this.turtle.random.nextInt(4) + 1);
+ BlockState iblockdata = (BlockState) Blocks.TURTLE_EGG.defaultBlockState().setValue(TurtleEggBlock.EGGS, layEggEvent.getEggCount()); // Paper
world.setBlock(blockposition1, iblockdata, 3);
world.gameEvent((Holder) GameEvent.BLOCK_PLACE, blockposition1, GameEvent.Context.of(this.turtle, iblockdata));
@@ -564,7 +567,7 @@ public class Turtle extends Animal {
@Override
public boolean canUse() {
- return this.turtle.isBaby() ? false : (this.turtle.hasEgg() ? true : (this.turtle.getRandom().nextInt(reducedTickDelay(700)) != 0 ? false : !this.turtle.getHomePos().closerToCenterThan(this.turtle.position(), 64.0D)));
+ return this.turtle.isBaby() ? false : (this.turtle.hasEgg() ? true : (this.turtle.getRandom().nextInt(reducedTickDelay(700)) != 0 ? false : !this.turtle.getHomePos().closerToCenterThan(this.turtle.position(), 64.0D))) && new com.destroystokyo.paper.event.entity.TurtleGoHomeEvent((org.bukkit.entity.Turtle) this.turtle.getBukkitEntity()).callEvent(); // Paper - Turtle API
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
index fac0317ff945db991e761ac8973f0d3d41ade26b..d44e6f4bb682d18c1497eee9fb2802f2bda6e840 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java
@@ -28,4 +28,31 @@ public class CraftTurtle extends CraftAnimals implements Turtle {
public boolean isLayingEgg() {
return this.getHandle().isLayingEgg();
}
+
+ // Paper start
+ @Override
+ public org.bukkit.Location getHome() {
+ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), this.getHandle().getHomePos());
+ }
+
+ @Override
+ public void setHome(org.bukkit.Location location) {
+ this.getHandle().setHomePos(io.papermc.paper.util.MCUtil.toBlockPosition(location));
+ }
+
+ @Override
+ public boolean isGoingHome() {
+ return this.getHandle().isGoingHome();
+ }
+
+ @Override
+ public boolean isDigging() {
+ return this.getHandle().isLayingEgg();
+ }
+
+ @Override
+ public void setHasEgg(boolean hasEgg) {
+ this.getHandle().setHasEgg(hasEgg);
+ }
+ // Paper end
}

View file

@ -1,46 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Caleb Bassham <caleb.bassham@gmail.com>
Date: Fri, 28 Sep 2018 02:32:19 -0500
Subject: [PATCH] Call player spectator target events and improve
implementation
Use a proper teleport for teleporting to entities in different
worlds.
Implementation improvements authored by Spottedleaf <Spottedleaf@users.noreply.github.com>
Validate that the target entity is valid and deny spectate
requests from frozen players.
Also, make sure the entity is spawned to the client before
sending the camera packet. If the entity isn't spawned clientside
when it receives the camera packet, then the client will not
spectate the target entity.
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index fa8640f961b93dc811296131dfda58faa1908add..15328d344a26f5c40011ee6ba0bc54dd5ab0b87b 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -2182,6 +2182,21 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
this.camera = (Entity) (entity == null ? this : entity);
if (entity1 != this.camera) {
+ // Paper start - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity
+ if (this.camera == this) {
+ com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent playerStopSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity());
+ if (!playerStopSpectatingEntityEvent.callEvent()) {
+ this.camera = entity1; // rollback camera entity again
+ return;
+ }
+ } else {
+ com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity(), entity.getBukkitEntity());
+ if (!playerStartSpectatingEntityEvent.callEvent()) {
+ this.camera = entity1; // rollback camera entity again
+ return;
+ }
+ }
+ // Paper end - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity
Level world = this.camera.level();
if (world instanceof ServerLevel) {

View file

@ -1,101 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Fri, 12 Oct 2018 14:10:46 -0500
Subject: [PATCH] Add more Witch API
== AT ==
public net.minecraft.world.entity.monster.Witch usingTime
diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java
index f6d01d21745391595d61b191832be4c28a3e58cb..b8ff1e3d280171378fe383bcc7c6a855d20ae5d1 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Witch.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java
@@ -151,21 +151,7 @@ public class Witch extends Raider implements RangedAttackMob {
}
if (holder != null) {
- // Paper start
- ItemStack potion = PotionContents.createItemStack(Items.POTION, holder);
- potion = org.bukkit.craftbukkit.event.CraftEventFactory.handleWitchReadyPotionEvent(this, potion);
- this.setItemSlot(EquipmentSlot.MAINHAND, potion);
- // Paper end
- this.usingTime = this.getMainHandItem().getUseDuration(this);
- this.setUsingItem(true);
- if (!this.isSilent()) {
- this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_DRINK, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F);
- }
-
- AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
-
- attributemodifiable.removeModifier(Witch.SPEED_MODIFIER_DRINKING_ID);
- attributemodifiable.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING);
+ this.setDrinkingPotion(PotionContents.createItemStack(Items.POTION, holder)); // Paper - logic moved into setDrinkingPotion, copy exact impl into the method and then comment out
}
}
@@ -177,6 +163,23 @@ public class Witch extends Raider implements RangedAttackMob {
super.aiStep();
}
+ // Paper start - moved to its own method
+ public void setDrinkingPotion(ItemStack potion) {
+ potion = org.bukkit.craftbukkit.event.CraftEventFactory.handleWitchReadyPotionEvent(this, potion);
+ this.setItemSlot(EquipmentSlot.MAINHAND, potion);
+ this.usingTime = this.getMainHandItem().getUseDuration(this);
+ this.setUsingItem(true);
+ if (!this.isSilent()) {
+ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_DRINK, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F);
+ }
+
+ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
+
+ attributemodifiable.removeModifier(Witch.SPEED_MODIFIER_DRINKING_ID);
+ attributemodifiable.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING);
+ }
+ // Paper end
+
@Override
public SoundEvent getCelebrateSound() {
return SoundEvents.WITCH_CELEBRATE;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java
index 524b5ba5995affc09eedf9a85d22e8b0b4efc156..4b3d783cabcb2de1a67d7fbfb6f525bfb493aed1 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java
@@ -2,6 +2,13 @@ package org.bukkit.craftbukkit.entity;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Witch;
+// Paper start
+import com.destroystokyo.paper.entity.CraftRangedEntity;
+import com.google.common.base.Preconditions;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.inventory.ItemStack;
+// Paper end
public class CraftWitch extends CraftRaider implements Witch, com.destroystokyo.paper.entity.CraftRangedEntity<net.minecraft.world.entity.monster.Witch> { // Paper
public CraftWitch(CraftServer server, net.minecraft.world.entity.monster.Witch entity) {
@@ -22,4 +29,23 @@ public class CraftWitch extends CraftRaider implements Witch, com.destroystokyo.
public boolean isDrinkingPotion() {
return this.getHandle().isDrinkingPotion();
}
+ // Paper start
+ public int getPotionUseTimeLeft() {
+ return getHandle().usingTime;
+ }
+
+ @Override
+ public void setPotionUseTimeLeft(int ticks) {
+ getHandle().usingTime = ticks;
+ }
+
+ public ItemStack getDrinkingPotion() {
+ return CraftItemStack.asCraftMirror(getHandle().getMainHandItem());
+ }
+
+ public void setDrinkingPotion(ItemStack potion) {
+ Preconditions.checkArgument(potion == null || potion.getType().isEmpty() || potion.getType() == Material.POTION, "must be potion, air, or null");
+ getHandle().setDrinkingPotion(CraftItemStack.asNMSCopy(potion));
+ }
+ // Paper end
}

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Wed, 10 Oct 2018 21:22:44 -0500
Subject: [PATCH] Check Drowned for Villager Aggression Config
diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
index d4b3ce2bb2021625c90a3f51c6f9da6056b2e2ff..cff1b5e0e3fd32d82157d5f13d83d4abdfad7378 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
@@ -81,7 +81,7 @@ public class Drowned extends Zombie implements RangedAttackMob {
this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D));
this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Drowned.class})).setAlertOthers(ZombifiedPiglin.class));
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::okTarget));
- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
+ if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper - Check drowned for villager aggression config
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false));
this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));

View file

@ -1,67 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Gabriele C <sgdc3.mail@gmail.com>
Date: Mon, 22 Oct 2018 17:34:10 +0200
Subject: [PATCH] Add option to prevent players from moving into unloaded
chunks #1551
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 5ed35d744a87290a03e9bf58143b5650501af0e6..6cf68d7173bc23c39261856f11cbd42de387bd60 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -494,9 +494,9 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
double d0 = entity.getX();
double d1 = entity.getY();
double d2 = entity.getZ();
- double d3 = ServerGamePacketListenerImpl.clampHorizontal(packet.getX());
- double d4 = ServerGamePacketListenerImpl.clampVertical(packet.getY());
- double d5 = ServerGamePacketListenerImpl.clampHorizontal(packet.getZ());
+ double d3 = ServerGamePacketListenerImpl.clampHorizontal(packet.getX()); final double toX = d3; // Paper - OBFHELPER
+ double d4 = ServerGamePacketListenerImpl.clampVertical(packet.getY()); final double toY = d4; // Paper - OBFHELPER
+ double d5 = ServerGamePacketListenerImpl.clampHorizontal(packet.getZ()); final double toZ = d5; // Paper - OBFHELPER
float f = Mth.wrapDegrees(packet.getYRot());
float f1 = Mth.wrapDegrees(packet.getXRot());
double d6 = d3 - this.vehicleFirstGoodX;
@@ -530,6 +530,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
+ // Paper start - Prevent moving into unloaded chunks
+ if (this.player.level().paperConfig().chunks.preventMovingIntoUnloadedChunks && (
+ !worldserver.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(this.player.position()))) ||
+ !worldserver.areChunksLoadedForMove(entity.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(entity.position())))
+ )) {
+ this.connection.send(new ClientboundMoveVehiclePacket(entity));
+ return;
+ }
+ // Paper end - Prevent moving into unloaded chunks
+
if (d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
// CraftBukkit end
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8});
@@ -1157,9 +1167,9 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
if (!this.updateAwaitingTeleport()) {
- double d0 = ServerGamePacketListenerImpl.clampHorizontal(packet.getX(this.player.getX()));
- double d1 = ServerGamePacketListenerImpl.clampVertical(packet.getY(this.player.getY()));
- double d2 = ServerGamePacketListenerImpl.clampHorizontal(packet.getZ(this.player.getZ()));
+ double d0 = ServerGamePacketListenerImpl.clampHorizontal(packet.getX(this.player.getX())); final double toX = d0; // Paper - OBFHELPER
+ double d1 = ServerGamePacketListenerImpl.clampVertical(packet.getY(this.player.getY())); final double toY = d1; // Paper - OBFHELPER
+ double d2 = ServerGamePacketListenerImpl.clampHorizontal(packet.getZ(this.player.getZ())); final double toZ = d2; // Paper - OBFHELPER
float f = Mth.wrapDegrees(packet.getYRot(this.player.getYRot()));
float f1 = Mth.wrapDegrees(packet.getXRot(this.player.getXRot()));
@@ -1217,6 +1227,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
} else {
speed = this.player.getAbilities().walkingSpeed * 10f;
}
+ // Paper start - Prevent moving into unloaded chunks
+ if (this.player.level().paperConfig().chunks.preventMovingIntoUnloadedChunks && (this.player.getX() != toX || this.player.getZ() != toZ) && !worldserver.areChunksLoadedForMove(this.player.getBoundingBox().expandTowards(new Vec3(toX, toY, toZ).subtract(this.player.position())))) {
+ this.internalTeleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot(), Collections.emptySet());
+ return;
+ }
+ // Paper end - Prevent moving into unloaded chunks
if (!this.player.isChangingDimension() && (!this.player.level().getGameRules().getBoolean(GameRules.RULE_DISABLE_ELYTRA_MOVEMENT_CHECK) || !flag)) {
float f2 = flag ? 300.0F : 100.0F;

View file

@ -1,18 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: GreenMeanie <GreenMeanieMC@gmail.com>
Date: Sat, 20 Oct 2018 22:34:02 -0400
Subject: [PATCH] Reset players airTicks on respawn
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 15328d344a26f5c40011ee6ba0bc54dd5ab0b87b..7cca5c778f9d20cfa6cb543c9afcf74779aaa355 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -2703,6 +2703,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
this.setHealth(this.getMaxHealth());
this.stopUsingItem(); // CraftBukkit - SPIGOT-6682: Clear active item on reset
+ this.setAirSupply(this.getMaxAirSupply()); // Paper - Reset players airTicks on respawn
this.setRemainingFireTicks(0);
this.fallDistance = 0;
this.foodData = new FoodData(this);

View file

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 23 Oct 2018 20:25:05 -0400
Subject: [PATCH] Don't sleep after profile lookups if not needed
Mojang was sleeping even if we had no more requests to go after
the current one finished, resulting in 100ms lost per profile lookup
diff --git a/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java
index b87546f0061458b2b919a1fe00dde1f4eea6cb3e..55dac5edf694b3bf82b475a71e3524a1bce98882 100644
--- a/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java
+++ b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java
@@ -44,6 +44,7 @@ public class YggdrasilGameProfileRepository implements GameProfileRepository {
.collect(Collectors.toSet());
final int page = 0;
+ boolean hasRequested = false; // Paper - Don't sleep after profile lookups if not needed
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
final List<String> normalizedRequest = request.stream().map(YggdrasilGameProfileRepository::normalizeName).toList();
@@ -75,6 +76,12 @@ public class YggdrasilGameProfileRepository implements GameProfileRepository {
LOGGER.debug("Couldn't find profile {}", name);
callback.onProfileLookupFailed(name, new ProfileNotFoundException("Server did not find the requested profile"));
}
+ // Paper start - Don't sleep after profile lookups if not needed
+ if (!hasRequested) {
+ hasRequested = true;
+ continue;
+ }
+ // Paper end - Don't sleep after profile lookups if not needed
try {
Thread.sleep(DELAY_BETWEEN_PAGES);

View file

@ -1,105 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 23 Oct 2018 23:14:38 -0400
Subject: [PATCH] Improve Server Thread Pool and Thread Priorities
Use a simple executor since Fork join is a much more complex pool
type and we are not using its capabilities.
Set thread priorities so main thread has above normal priority over
server threads
Allow usage of a single thread executor by not using ForkJoin so single core CPU's
and reduce worldgen thread worker count for low core count CPUs.
== AT ==
public net.minecraft.Util onThreadException(Ljava/lang/Thread;Ljava/lang/Throwable;)V
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/src/main/java/io/papermc/paper/util/ServerWorkerThread.java b/src/main/java/io/papermc/paper/util/ServerWorkerThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..b60f59cf5cc8eb84a6055b7861857dece7f2501b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/ServerWorkerThread.java
@@ -0,0 +1,14 @@
+package io.papermc.paper.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import net.minecraft.Util;
+
+public class ServerWorkerThread extends Thread {
+ private static final AtomicInteger threadId = new AtomicInteger(1);
+ public ServerWorkerThread(Runnable target, String poolName, int prioritityModifier) {
+ super(target, "Worker-" + poolName + "-" + threadId.getAndIncrement());
+ setPriority(Thread.NORM_PRIORITY+prioritityModifier); // Deprioritize over main
+ this.setDaemon(true);
+ this.setUncaughtExceptionHandler(Util::onThreadException);
+ }
+}
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 54562fa04d14a937451ea7aa9d80194f2c31b471..4cf88f6d815d60cfbf8e4ecf9d96d0cfadd0620b 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -89,7 +89,7 @@ public class Util {
private static final int DEFAULT_MAX_THREADS = 255;
private static final int DEFAULT_SAFE_FILE_OPERATION_RETRIES = 10;
private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads";
- private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main");
+ private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - Perf: add priority
private static final ExecutorService IO_POOL = makeIoExecutor("IO-Worker-", false);
private static final ExecutorService DOWNLOAD_POOL = makeIoExecutor("Download-", true);
// Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
@@ -160,15 +160,27 @@ public class Util {
return FILENAME_DATE_TIME_FORMATTER.format(ZonedDateTime.now());
}
- private static ExecutorService makeExecutor(String name) {
- int i = Mth.clamp(Runtime.getRuntime().availableProcessors() - 1, 1, getMaxThreads());
+ private static ExecutorService makeExecutor(String s, int priorityModifier) { // Paper - Perf: add priority
+ // Paper start - Perf: use simpler thread pool that allows 1 thread and reduce worldgen thread worker count for low core count CPUs
+ int cpus = Runtime.getRuntime().availableProcessors() / 2;
+ int i;
+ if (cpus <= 4) {
+ i = cpus <= 2 ? 1 : 2;
+ } else if (cpus <= 8) {
+ // [5, 8]
+ i = Math.max(3, cpus - 2);
+ } else {
+ i = cpus * 2 / 3;
+ }
+ i = Math.min(8, i);
+ i = Integer.getInteger("Paper.WorkerThreadCount", i);
ExecutorService executorService;
if (i <= 0) {
executorService = MoreExecutors.newDirectExecutorService();
} else {
- AtomicInteger atomicInteger = new AtomicInteger(1);
- executorService = new ForkJoinPool(i, pool -> {
- ForkJoinWorkerThread forkJoinWorkerThread = new ForkJoinWorkerThread(pool) {
+ executorService = new java.util.concurrent.ThreadPoolExecutor(i, i,0L, TimeUnit.MILLISECONDS, new java.util.concurrent.LinkedBlockingQueue<>(), target -> new io.papermc.paper.util.ServerWorkerThread(target, s, priorityModifier));
+ }
+ /*
@Override
protected void onTermination(Throwable throwable) {
if (throwable != null) {
@@ -184,6 +196,7 @@ public class Util {
return forkJoinWorkerThread;
}, Util::onThreadException, true);
}
+ }*/ // Paper end - Perf: use simpler thread pool
return executorService;
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 2d5ae71c143556a938f078d2fb84cab7bd4f789b..f3f3f80f14bc1df13b80033aa143fcccab6f3a31 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -322,6 +322,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
thread.setUncaughtExceptionHandler((thread1, throwable) -> {
MinecraftServer.LOGGER.error("Uncaught exception in server thread", throwable);
});
+ thread.setPriority(Thread.NORM_PRIORITY+2); // Paper - Perf: Boost priority
if (Runtime.getRuntime().availableProcessors() > 4) {
thread.setPriority(8);
}

View file

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 2 Nov 2018 23:11:51 -0400
Subject: [PATCH] Optimize World Time Updates
Splits time updates into incremental updates as well as does
the updates per world, so that we can re-use the same packet
object for every player unless they have per-player time enabled.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index f3f3f80f14bc1df13b80033aa143fcccab6f3a31..699191356a9d873fa6bbe04ea3e5db91eb2db41d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1560,12 +1560,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper
// Send time updates to everyone, it will get the right time from the world the player is in.
- if (this.tickCount % 20 == 0) {
- for (int i = 0; i < this.getPlayerList().players.size(); ++i) {
- ServerPlayer entityplayer = (ServerPlayer) this.getPlayerList().players.get(i);
- entityplayer.connection.send(new ClientboundSetTimePacket(entityplayer.level().getGameTime(), entityplayer.getPlayerTime(), entityplayer.level().getGameRules().getBoolean(GameRules.RULE_DAYLIGHT))); // Add support for per player time
+ // Paper start - Perf: Optimize time updates
+ for (final ServerLevel level : this.getAllLevels()) {
+ final boolean doDaylight = level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT);
+ final long dayTime = level.getDayTime();
+ long worldTime = level.getGameTime();
+ final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight);
+ for (Player entityhuman : level.players()) {
+ if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) {
+ continue;
+ }
+ ServerPlayer entityplayer = (ServerPlayer) entityhuman;
+ long playerTime = entityplayer.getPlayerTime();
+ ClientboundSetTimePacket packet = (playerTime == dayTime) ? worldPacket :
+ new ClientboundSetTimePacket(worldTime, playerTime, doDaylight);
+ entityplayer.connection.send(packet); // Add support for per player time
}
}
+ // Paper end - Perf: Optimize time updates
MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper
while (iterator.hasNext()) {

View file

@ -1,358 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 5 Nov 2018 04:23:51 +0000
Subject: [PATCH] Restore custom InventoryHolder support
Upstream removed the ability to consistently use a custom InventoryHolder,
However, the implementation does not use an InventoryHolder in any form
outside of custom inventories.
== AT ==
public-f net.minecraft.world.inventory.AbstractContainerMenu dataSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu remoteDataSlots
Co-authored-by: Shane Freeder <theboyetronic@gmail.com>
diff --git a/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java b/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java
new file mode 100644
index 0000000000000000000000000000000000000000..224d4b2cc45b0d02230a76caee9c88573a448b4c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java
@@ -0,0 +1,141 @@
+package io.papermc.paper.inventory;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.minecraft.world.Container;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.InventoryHolder;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+import java.util.List;
+
+@DefaultQualifier(NonNull.class)
+public final class PaperInventoryCustomHolderContainer implements Container {
+
+ private final InventoryHolder owner;
+ private final Container delegate;
+ private final InventoryType type;
+ private final String title;
+ private final Component adventure$title;
+
+ public PaperInventoryCustomHolderContainer(InventoryHolder owner, Container delegate, InventoryType type) {
+ this.owner = owner;
+ this.delegate = delegate;
+ this.type = type;
+ @Nullable Component adventure$title = null;
+ if (delegate instanceof BaseContainerBlockEntity blockEntity) {
+ adventure$title = blockEntity.getCustomName() != null ? PaperAdventure.asAdventure(blockEntity.getCustomName()) : null;
+ }
+ if (adventure$title == null) {
+ adventure$title = type.defaultTitle();
+ }
+ this.adventure$title = adventure$title;
+ this.title = LegacyComponentSerializer.legacySection().serialize(this.adventure$title);
+ }
+
+ public Component title() {
+ return this.adventure$title;
+ }
+
+ public String getTitle() {
+ return this.title;
+ }
+
+ public InventoryType getType() {
+ return this.type;
+ }
+
+ @Override
+ public int getContainerSize() {
+ return this.delegate.getContainerSize();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.delegate.isEmpty();
+ }
+
+ @Override
+ public ItemStack getItem(int slot) {
+ return this.delegate.getItem(slot);
+ }
+
+ @Override
+ public ItemStack removeItem(int slot, int amount) {
+ return this.delegate.removeItem(slot, amount);
+ }
+
+ @Override
+ public ItemStack removeItemNoUpdate(int slot) {
+ return this.delegate.removeItemNoUpdate(slot);
+ }
+
+ @Override
+ public void setItem(int slot, ItemStack stack) {
+ this.delegate.setItem(slot, stack);
+ }
+
+ @Override
+ public int getMaxStackSize() {
+ return this.delegate.getMaxStackSize();
+ }
+
+ @Override
+ public void setChanged() {
+ this.delegate.setChanged();
+ }
+
+ @Override
+ public boolean stillValid(Player player) {
+ return this.delegate.stillValid(player);
+ }
+
+ @Override
+ public List<ItemStack> getContents() {
+ return this.delegate.getContents();
+ }
+
+ @Override
+ public void onOpen(CraftHumanEntity who) {
+ this.delegate.onOpen(who);
+ }
+
+ @Override
+ public void onClose(CraftHumanEntity who) {
+ this.delegate.onClose(who);
+ }
+
+ @Override
+ public List<HumanEntity> getViewers() {
+ return this.delegate.getViewers();
+ }
+
+ @Override
+ public InventoryHolder getOwner() {
+ return this.owner;
+ }
+
+ @Override
+ public void setMaxStackSize(int size) {
+ this.delegate.setMaxStackSize(size);
+ }
+
+ @Override
+ public Location getLocation() {
+ return this.delegate.getLocation();
+ }
+
+ @Override
+ public void clearContent() {
+ this.delegate.clearContent();
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
index 1a2329021a6b29777c637ee4dc8cd69ed18001c9..674e3a827f8fb64e5c0beefb3c1874d6e8aee4e5 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
@@ -49,7 +49,7 @@ public class CraftContainer extends AbstractContainerMenu {
public CraftContainer(final Inventory inventory, final Player player, int id) {
this(new CraftAbstractInventoryView() {
- private final String originalTitle = (inventory instanceof CraftInventoryCustom) ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).getTitle() : inventory.getType().getDefaultTitle();
+ private final String originalTitle = inventory instanceof CraftInventoryCustom ? ((CraftInventoryCustom) inventory).getTitle() : inventory.getType().getDefaultTitle(); // Paper
private String title = this.originalTitle;
@Override
@@ -75,7 +75,7 @@ public class CraftContainer extends AbstractContainerMenu {
// Paper start
@Override
public net.kyori.adventure.text.Component title() {
- return inventory instanceof CraftInventoryCustom ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).title() : net.kyori.adventure.text.Component.text(inventory.getType().getDefaultTitle());
+ return inventory instanceof CraftInventoryCustom custom ? custom.title() : inventory.getType().defaultTitle(); // Paper
}
// Paper end
@@ -214,6 +214,10 @@ public class CraftContainer extends AbstractContainerMenu {
this.lastSlots = this.delegate.lastSlots;
this.slots = this.delegate.slots;
this.remoteSlots = this.delegate.remoteSlots;
+ // Paper start - copy data slots for InventoryView#set/getProperty
+ this.dataSlots = this.delegate.dataSlots;
+ this.remoteDataSlots = this.delegate.remoteDataSlots;
+ // Paper end
}
// SPIGOT-4598 - we should still delegate the shift click handler
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
index 6a47c6adb721f0c6737150d8b0ee18ab70f5f281..75eb794f796b31c0c5ef80a6d27a56711a522f5e 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
@@ -497,6 +497,10 @@ public class CraftInventory implements Inventory {
return InventoryType.BREWING;
} else if (this.inventory instanceof CraftInventoryCustom.MinecraftInventory) {
return ((CraftInventoryCustom.MinecraftInventory) this.inventory).getType();
+ // Paper start
+ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer holderContainer) {
+ return holderContainer.getType();
+ // Paper end
} else if (this.inventory instanceof PlayerEnderChestContainer) {
return InventoryType.ENDER_CHEST;
} else if (this.inventory instanceof MerchantContainer) {
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java
index fc0e1212022d1aa3506699b60ef338196eb54eba..da1c1fe0faf6819b15a81d6ad53370948e5f984f 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java
@@ -15,6 +15,11 @@ import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.InventoryHolder;
public class CraftInventoryCustom extends CraftInventory {
+ // Paper start
+ public CraftInventoryCustom(InventoryHolder owner, InventoryType type, Container delegate) {
+ super(new io.papermc.paper.inventory.PaperInventoryCustomHolderContainer(owner, delegate, type));
+ }
+ // Paper end
public CraftInventoryCustom(InventoryHolder owner, InventoryType type) {
super(new MinecraftInventory(owner, type));
}
@@ -42,6 +47,27 @@ public class CraftInventoryCustom extends CraftInventory {
public CraftInventoryCustom(InventoryHolder owner, int size, String title) {
super(new MinecraftInventory(owner, size, title));
}
+ // Paper start
+ public String getTitle() {
+ if (this.inventory instanceof MinecraftInventory minecraftInventory) {
+ return minecraftInventory.getTitle();
+ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer customHolderContainer) {
+ return customHolderContainer.getTitle();
+ } else {
+ throw new UnsupportedOperationException(this.inventory.getClass() + " isn't a recognized Container type here");
+ }
+ }
+
+ public net.kyori.adventure.text.Component title() {
+ if (this.inventory instanceof MinecraftInventory minecraftInventory) {
+ return minecraftInventory.title();
+ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer customHolderContainer) {
+ return customHolderContainer.title();
+ } else {
+ throw new UnsupportedOperationException(this.inventory.getClass() + " isn't a recognized Container type here");
+ }
+ }
+ // Paper end
static class MinecraftInventory implements Container {
private final NonNullList<ItemStack> items;
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java
index 7bc082d08a3d577481046818f0d58133413fc723..a6c758c5c5da2fb3f2d251bc109f72a5d8b0eb14 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java
@@ -28,7 +28,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
@Override
public Inventory createInventory(InventoryHolder holder, InventoryType type) {
- return this.getInventory(this.getTileEntity());
+ return this.getInventory(holder, type, this.getTileEntity()); // Paper
}
// Paper start
@@ -39,7 +39,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
((RandomizableContainerBlockEntity) te).name = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
}
- return getInventory(te);
+ return this.getInventory(owner, type, te); // Paper
}
// Paper end
@@ -50,10 +50,18 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
((RandomizableContainerBlockEntity) te).name = CraftChatMessage.fromStringOrNull(title);
}
- return this.getInventory(te);
+ return this.getInventory(holder, type, te); // Paper
}
+ @Deprecated // Paper - use getInventory with owner and type
public Inventory getInventory(Container tileEntity) {
+ // Paper start
+ return this.getInventory(null, null, tileEntity);
+ }
+
+ public Inventory getInventory(InventoryHolder owner, InventoryType type, Container tileEntity) { // Paper
+ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper
+ // Paper end
return new CraftInventory(tileEntity);
}
@@ -69,8 +77,8 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
@Override
public Inventory createInventory(InventoryHolder owner, InventoryType type, net.kyori.adventure.text.Component title) {
Container tileEntity = getTileEntity();
- ((AbstractFurnaceBlockEntity) tileEntity).setCustomName(io.papermc.paper.adventure.PaperAdventure.asVanilla(title));
- return getInventory(tileEntity);
+ ((AbstractFurnaceBlockEntity) tileEntity).name = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
+ return this.getInventory(owner, type, tileEntity); // Paper
}
// Paper end
@@ -78,11 +86,19 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) {
Container tileEntity = this.getTileEntity();
((AbstractFurnaceBlockEntity) tileEntity).name = CraftChatMessage.fromStringOrNull(title);
- return this.getInventory(tileEntity);
+ return this.getInventory(owner, type, tileEntity); // Paper
}
@Override
public Inventory getInventory(Container tileEntity) {
+ // Paper start
+ return getInventory(null, null, tileEntity);
+ }
+
+ @Override
+ public Inventory getInventory(InventoryHolder owner, InventoryType type, net.minecraft.world.Container tileEntity) { // Paper
+ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper
+ // Paper end
return new CraftInventoryFurnace((AbstractFurnaceBlockEntity) tileEntity);
}
}
@@ -102,7 +118,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
if (tileEntity instanceof BrewingStandBlockEntity) {
((BrewingStandBlockEntity) tileEntity).name = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
}
- return getInventory(tileEntity);
+ return this.getInventory(owner, type, tileEntity); // Paper
}
// Paper end
@@ -113,11 +129,19 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
if (tileEntity instanceof BrewingStandBlockEntity) {
((BrewingStandBlockEntity) tileEntity).name = CraftChatMessage.fromStringOrNull(title);
}
- return this.getInventory(tileEntity);
+ return this.getInventory(holder, type, tileEntity); // Paper
}
@Override
public Inventory getInventory(Container tileEntity) {
+ // Paper start
+ return getInventory(null, null, tileEntity);
+ }
+
+ @Override
+ public Inventory getInventory(InventoryHolder owner, InventoryType type, net.minecraft.world.Container tileEntity) { // Paper
+ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper
+ // Paper end
return new CraftInventoryBrewer(tileEntity);
}
}

View file

@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sat, 10 Nov 2018 05:15:21 +0000
Subject: [PATCH] Fix SpongeAbsortEvent handling
Only process drops when the block is actually going to be removed
diff --git a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
index 2e54730deee3149517a535f15ef6c0628ba659c2..902825ec9ea05f4418b45f56a008d73f217bd178 100644
--- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java
@@ -134,7 +134,11 @@ public class SpongeBlock extends Block {
} else if (iblockdata.is(Blocks.KELP) || iblockdata.is(Blocks.KELP_PLANT) || iblockdata.is(Blocks.SEAGRASS) || iblockdata.is(Blocks.TALL_SEAGRASS)) {
BlockEntity tileentity = iblockdata.hasBlockEntity() ? world.getBlockEntity(blockposition1) : null;
+ // Paper start - Fix SpongeAbsortEvent handling
+ if (block.getHandle().isAir()) {
dropResources(iblockdata, world, blockposition1, tileentity);
+ }
+ // Paper end - Fix SpongeAbsortEvent handling
}
}
world.setBlock(blockposition1, block.getHandle(), block.getFlag());

View file

@ -1,77 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 11 Nov 2018 21:01:09 +0000
Subject: [PATCH] Don't allow digging into unloaded chunks
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index a5b0efd6142075ca1ecb604afbc1d0162199e7a4..da9e864520150acd8027545672aa476be414bb4d 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -124,8 +124,8 @@ public class ServerPlayerGameMode {
BlockState iblockdata;
if (this.hasDelayedDestroy) {
- iblockdata = this.level.getBlockState(this.delayedDestroyPos);
- if (iblockdata.isAir()) {
+ iblockdata = this.level.getBlockStateIfLoaded(this.delayedDestroyPos); // Paper - Don't allow digging into unloaded chunks
+ if (iblockdata == null || iblockdata.isAir()) { // Paper - Don't allow digging into unloaded chunks
this.hasDelayedDestroy = false;
} else {
float f = this.incrementDestroyProgress(iblockdata, this.delayedDestroyPos, this.delayedTickStart);
@@ -136,7 +136,13 @@ public class ServerPlayerGameMode {
}
}
} else if (this.isDestroyingBlock) {
- iblockdata = this.level.getBlockState(this.destroyPos);
+ // Paper start - Don't allow digging into unloaded chunks; don't want to do same logic as above, return instead
+ iblockdata = this.level.getBlockStateIfLoaded(this.destroyPos);
+ if (iblockdata == null) {
+ this.isDestroyingBlock = false;
+ return;
+ }
+ // Paper end - Don't allow digging into unloaded chunks
if (iblockdata.isAir()) {
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
this.lastSentState = -1;
@@ -165,6 +171,7 @@ public class ServerPlayerGameMode {
public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction direction, int worldHeight, int sequence) {
if (!this.player.canInteractWithBlock(pos, 1.0D)) {
+ if (true) return; // Paper - Don't allow digging into unloaded chunks; Don't notify if unreasonably far away
this.debugLogging(pos, false, sequence, "too far");
} else if (pos.getY() >= worldHeight) {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
@@ -307,10 +314,12 @@ public class ServerPlayerGameMode {
this.debugLogging(pos, true, sequence, "stopped destroying");
} else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) {
this.isDestroyingBlock = false;
- if (!Objects.equals(this.destroyPos, pos)) {
+ if (!Objects.equals(this.destroyPos, pos) && !BlockPos.ZERO.equals(this.destroyPos)) { // Paper
ServerPlayerGameMode.LOGGER.debug("Mismatch in destroy block pos: {} {}", this.destroyPos, pos); // CraftBukkit - SPIGOT-5457 sent by client when interact event cancelled
- this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
- this.debugLogging(pos, true, sequence, "aborted mismatched destroying");
+ BlockState type = this.level.getBlockStateIfLoaded(this.destroyPos); // Paper - don't load unloaded chunks for stale records here
+ if (type != null) this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
+ if (type != null) this.debugLogging(pos, true, sequence, "aborted mismatched destroying");
+ this.destroyPos = BlockPos.ZERO; // Paper
}
this.level.destroyBlockProgress(this.player.getId(), pos, -1);
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 6cf68d7173bc23c39261856f11cbd42de387bd60..4942aa857ecc5762c9b0b1662eefaeae4daab80d 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1595,6 +1595,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
case START_DESTROY_BLOCK:
case ABORT_DESTROY_BLOCK:
case STOP_DESTROY_BLOCK:
+ // Paper start - Don't allow digging into unloaded chunks
+ if (this.player.level().getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) == null) {
+ this.player.connection.ackBlockChangesUpTo(packet.getSequence());
+ return;
+ }
+ // Paper end - Don't allow digging into unloaded chunks
this.player.gameMode.handleBlockBreakAction(blockposition, packetplayinblockdig_enumplayerdigtype, packet.getDirection(), this.player.level().getMaxBuildHeight(), packet.getSequence());
this.player.connection.ackBlockChangesUpTo(packet.getSequence());
return;

View file

@ -1,40 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 18 Nov 2018 19:49:56 +0000
Subject: [PATCH] Make the default permission message configurable
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
index 5b070d158760789bbcaa984426a55d20767abe4a..e1820a339452cd3388dd7cbb928c5f58779a77b6 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -73,7 +73,7 @@ public final class PaperCommand extends Command {
if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.paper")) {
return true;
}
- sender.sendMessage(text("I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.", RED));
+ sender.sendMessage(Bukkit.permissionMessage());
return false;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 954b3725d4f702f284cd8712305a3f97fb90b9c1..7407d9f3480e0e44be44c84831864e511dfdebc2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2874,6 +2874,16 @@ public final class CraftServer implements Server {
return io.papermc.paper.configuration.GlobalConfiguration.get().commands.suggestPlayerNamesWhenNullTabCompletions;
}
+ @Override
+ public String getPermissionMessage() {
+ return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacyAmpersand().serialize(io.papermc.paper.configuration.GlobalConfiguration.get().messages.noPermission);
+ }
+
+ @Override
+ public net.kyori.adventure.text.Component permissionMessage() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().messages.noPermission;
+ }
+
@Override
public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) {
return createProfile(uuid, null);

View file

@ -1,166 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Thu, 15 Nov 2018 13:38:37 +0000
Subject: [PATCH] force entity dismount during teleportation
Entities must be dismounted before teleportation in order to avoid
multiple issues in the server with regards to teleportation, shamefully,
too many plugins rely on the events firing, which means that not firing
these events caues more issues than it solves;
In order to counteract this, Entity dismount/exit vehicle events have
been modified to supress cancellation (and has a method to allow plugins
to check if this has been set), noting that cancellation will be silently
surpressed given that plugins are not expecting this event to not be cancellable.
This is a far from ideal scenario, however: given the current state of this
event and other alternatives causing issues elsewhere, I believe that
this is going to be the best soultion all around.
Improvements/suggestions welcome!
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 7cca5c778f9d20cfa6cb543c9afcf74779aaa355..c2571855ca6a8ecd144b8fbb97d601d1808e8d61 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -2522,9 +2522,15 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
@Override
public void stopRiding() {
+ // Paper start - Force entity dismount during teleportation
+ this.stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end - Force entity dismount during teleportation
Entity entity = this.getVehicle();
- super.stopRiding();
+ super.stopRiding(suppressCancellation); // Paper - Force entity dismount during teleportation
if (entity instanceof LivingEntity entityliving) {
Iterator iterator = entityliving.getActiveEffects().iterator();
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 7fd1a75ba0068ee3ca6c29a550a9a1b33c5cacc5..f330ddca00ed11bf76ae825820423b94920013b9 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2704,17 +2704,28 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public void removeVehicle() {
+ // Paper start - Force entity dismount during teleportation
+ this.removeVehicle(false);
+ }
+ public void removeVehicle(boolean suppressCancellation) {
+ // Paper end - Force entity dismount during teleportation
if (this.vehicle != null) {
Entity entity = this.vehicle;
this.vehicle = null;
- if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit
+ if (!entity.removePassenger(this, suppressCancellation)) this.vehicle = entity; // CraftBukkit // Paper - Force entity dismount during teleportation
}
}
public void stopRiding() {
- this.removeVehicle();
+ // Paper start - Force entity dismount during teleportation
+ this.stopRiding(false);
+ }
+
+ public void stopRiding(boolean suppressCancellation) {
+ this.removeVehicle(suppressCancellation);
+ // Paper end - Force entity dismount during teleportation
}
protected void addPassenger(Entity passenger) {
@@ -2739,7 +2750,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
}
- protected boolean removePassenger(Entity entity) { // CraftBukkit
+ // Paper start - Force entity dismount during teleportation
+ protected boolean removePassenger(Entity entity) { return removePassenger(entity, false);}
+ protected boolean removePassenger(Entity entity, boolean suppressCancellation) { // CraftBukkit
+ // Paper end - Force entity dismount during teleportation
if (entity.getVehicle() == this) {
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
} else {
@@ -2749,7 +2763,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
VehicleExitEvent event = new VehicleExitEvent(
(Vehicle) this.getBukkitEntity(),
- (LivingEntity) entity.getBukkitEntity()
+ (LivingEntity) entity.getBukkitEntity(), !suppressCancellation // Paper - Force entity dismount during teleportation
);
// Suppress during worldgen
if (this.valid) {
@@ -2762,7 +2776,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
}
- EntityDismountEvent event = new EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity());
+ EntityDismountEvent event = new EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity(), !suppressCancellation); // Paper - Force entity dismount during teleportation
// Suppress during worldgen
if (this.valid) {
Bukkit.getPluginManager().callEvent(event);
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 689aaf4ceedc598fe71db726215cceae6cc97296..fad0445628499ac14cd9d8ab7f618c490885e798 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3619,9 +3619,15 @@ public abstract class LivingEntity extends Entity implements Attackable {
@Override
public void stopRiding() {
+ // Paper start - Force entity dismount during teleportation
+ this.stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end - Force entity dismount during teleportation
Entity entity = this.getVehicle();
- super.stopRiding();
+ super.stopRiding(suppressCancellation); // Paper - Force entity dismount during teleportation
if (entity != null && entity != this.getVehicle() && !this.level().isClientSide) {
this.dismountVehicle(entity);
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
index 882236c8ebad90ed2adc873de4dda3b7f3f869d9..632b74e84d6ee58da8806e30b75e16fb864afa64 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
@@ -288,7 +288,13 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
@Override
public void stopRiding() {
- super.stopRiding();
+ // Paper start - Force entity dismount during teleportation
+ this.stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ super.stopRiding(suppressCancellation);
+ // Paper end - Force entity dismount during teleportation
if (this.level().isClientSide) {
this.clientOldAttachPosition = this.blockPosition();
}
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index b70fbc1a93271bbf28402afbe9c6e538a4b6e9aa..fa906334a1c569748d3f2dc073ec03a85bd09d3b 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1161,7 +1161,13 @@ public abstract class Player extends LivingEntity {
@Override
public void removeVehicle() {
- super.removeVehicle();
+ // Paper start - Force entity dismount during teleportation
+ this.removeVehicle(false);
+ }
+ @Override
+ public void removeVehicle(boolean suppressCancellation) {
+ super.removeVehicle(suppressCancellation);
+ // Paper end - Force entity dismount during teleportation
this.boardingCooldown = 0;
}

View file

@ -1,117 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sun, 7 Oct 2018 04:29:59 -0500
Subject: [PATCH] Add more Zombie API
== AT ==
public net.minecraft.world.entity.monster.Zombie isSunSensitive()Z
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index d97c3c139f10a45febc0cfb1057ff6e33266228e..d981f8679149669f6ca4ea950d744149974532b2 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -97,6 +97,7 @@ public class Zombie extends Monster {
private int inWaterTime;
public int conversionTime;
private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+ private boolean shouldBurnInDay = true; // Paper - Add more Zombie API
public Zombie(EntityType<? extends Zombie> type, Level world) {
super(type, world);
@@ -267,6 +268,12 @@ public class Zombie extends Monster {
super.aiStep();
}
+ // Paper start - Add more Zombie API
+ public void stopDrowning() {
+ this.conversionTime = -1;
+ this.getEntityData().set(Zombie.DATA_DROWNED_CONVERSION_ID, false);
+ }
+ // Paper end - Add more Zombie API
public void startUnderWaterConversion(int ticksUntilWaterConversion) {
this.lastTick = MinecraftServer.currentTick; // CraftBukkit
this.conversionTime = ticksUntilWaterConversion;
@@ -296,9 +303,15 @@ public class Zombie extends Monster {
}
public boolean isSunSensitive() {
- return true;
+ return this.shouldBurnInDay; // Paper - Add more Zombie API
}
+ // Paper start - Add more Zombie API
+ public void setShouldBurnInDay(boolean shouldBurnInDay) {
+ this.shouldBurnInDay = shouldBurnInDay;
+ }
+ // Paper end - Add more Zombie API
+
@Override
public boolean hurt(DamageSource source, float amount) {
if (!super.hurt(source, amount)) {
@@ -417,6 +430,7 @@ public class Zombie extends Monster {
nbt.putBoolean("CanBreakDoors", this.canBreakDoors());
nbt.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1);
nbt.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1);
+ nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API
}
@Override
@@ -428,6 +442,11 @@ public class Zombie extends Monster {
if (nbt.contains("DrownedConversionTime", 99) && nbt.getInt("DrownedConversionTime") > -1) {
this.startUnderWaterConversion(nbt.getInt("DrownedConversionTime"));
}
+ // Paper start - Add more Zombie API
+ if (nbt.contains("Paper.ShouldBurnInDay")) {
+ this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay");
+ }
+ // Paper end - Add more Zombie API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
index 99dcaa827831a40ea46453f502d8b6ccb107f0ad..4412c913123f7521f449c98b60378e8d3b1671ce 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java
@@ -87,6 +87,42 @@ public class CraftZombie extends CraftMonster implements Zombie {
@Override
public void setAgeLock(boolean b) {
}
+ // Paper start
+ @Override
+ public boolean isDrowning() {
+ return getHandle().isUnderWaterConverting();
+ }
+
+ @Override
+ public void startDrowning(int drownedConversionTime) {
+ getHandle().startUnderWaterConversion(drownedConversionTime);
+ }
+
+ @Override
+ public void stopDrowning() {
+ getHandle().stopDrowning();
+ }
+
+ @Override
+ public boolean shouldBurnInDay() {
+ return getHandle().isSunSensitive();
+ }
+
+ @Override
+ public boolean isArmsRaised() {
+ return getHandle().isAggressive();
+ }
+
+ @Override
+ public void setArmsRaised(final boolean raised) {
+ getHandle().setAggressive(raised);
+ }
+
+ @Override
+ public void setShouldBurnInDay(boolean shouldBurnInDay) {
+ getHandle().setShouldBurnInDay(shouldBurnInDay);
+ }
+ // Paper end
@Override
public boolean getAgeLock() {

View file

@ -1,72 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 16 Nov 2018 23:08:50 -0500
Subject: [PATCH] Book size limits
Puts some limits on the size of books.
diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundEditBookPacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundEditBookPacket.java
index ed61767a64cdce37dc7c226ebd0d693a60de24a9..f634a830a2b58a419e84f969bd53eeae4f4513bb 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ServerboundEditBookPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundEditBookPacket.java
@@ -16,9 +16,9 @@ public record ServerboundEditBookPacket(int slot, List<String> pages, Optional<S
public static final StreamCodec<FriendlyByteBuf, ServerboundEditBookPacket> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
ServerboundEditBookPacket::slot,
- ByteBufCodecs.stringUtf8(8192).apply(ByteBufCodecs.list(200)),
+ ByteBufCodecs.stringUtf8(net.minecraft.world.item.component.WritableBookContent.PAGE_EDIT_LENGTH).apply(ByteBufCodecs.list(net.minecraft.world.item.component.WritableBookContent.MAX_PAGES)), // Paper - limit books
ServerboundEditBookPacket::pages,
- ByteBufCodecs.stringUtf8(128).apply(ByteBufCodecs::optional),
+ ByteBufCodecs.stringUtf8(net.minecraft.world.item.component.WrittenBookContent.TITLE_MAX_LENGTH).apply(ByteBufCodecs::optional), // Paper - limit books
ServerboundEditBookPacket::title,
ServerboundEditBookPacket::new
);
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 4942aa857ecc5762c9b0b1662eefaeae4daab80d..577f0e290f66afd2ded18714fb8b5f62a7a9aa71 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1043,6 +1043,44 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@Override
public void handleEditBook(ServerboundEditBookPacket packet) {
+ // Paper start - Book size limits
+ final io.papermc.paper.configuration.type.number.IntOr.Disabled pageMax = io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.pageMax;
+ if (!this.cserver.isPrimaryThread() && pageMax.enabled()) {
+ final List<String> pageList = packet.pages();
+ long byteTotal = 0;
+ final int maxBookPageSize = pageMax.intValue();
+ final double multiplier = Math.clamp(io.papermc.paper.configuration.GlobalConfiguration.get().itemValidation.bookSize.totalMultiplier, 0.3D, 1D);
+ long byteAllowed = maxBookPageSize;
+ for (final String page : pageList) {
+ final int byteLength = page.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
+ byteTotal += byteLength;
+ final int length = page.length();
+ int multiByteCharacters = 0;
+ if (byteLength != length) {
+ // Count the number of multi byte characters
+ for (final char c : page.toCharArray()) {
+ if (c > 127) {
+ multiByteCharacters++;
+ }
+ }
+ }
+
+ // Allow pages with fewer characters to consume less of the allowed byte quota
+ byteAllowed += maxBookPageSize * Math.clamp((double) length / 255D, 0.1D, 1) * multiplier;
+
+ if (multiByteCharacters > 1) {
+ // Penalize multibyte characters
+ byteAllowed -= multiByteCharacters;
+ }
+ }
+
+ if (byteTotal > byteAllowed) {
+ ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size());
+ this.disconnect(Component.literal("Book too large!"));
+ return;
+ }
+ }
+ // Paper end - Book size limits
// CraftBukkit start
if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
this.disconnect(Component.literal("Book edited too quickly!"));

View file

@ -1,83 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 7 Oct 2018 12:05:28 -0700
Subject: [PATCH] Add PlayerConnectionCloseEvent
This event is invoked when a player has disconnected. It is guaranteed that,
if the server is in online-mode, that the provided uuid and username have been
validated.
The event is invoked for players who have not yet logged into the world, whereas
PlayerQuitEvent is only invoked on players who have logged into the world.
The event is invoked for players who have already logged into the world,
although whether or not the player exists in the world at the time of
firing is undefined. (That is, whether the plugin can retrieve a Player object
using the event parameters is undefined). However, it is guaranteed that this
event is invoked AFTER PlayerQuitEvent, if the player has already logged into
the world.
This event is guaranteed to never fire unless AsyncPlayerPreLoginEvent has
been called beforehand, and this event may not be called in parallel with
AsyncPlayerPreLoginEvent for the same connection.
Cancelling the AsyncPlayerPreLoginEvent guarantees the corresponding
PlayerConnectionCloseEvent is never called.
The event may be invoked asynchronously or synchronously. As it stands,
it is never invoked asynchronously. However, plugins should check
Event#isAsynchronous to be future-proof.
On purpose, the deprecated PlayerPreLoginEvent event is left out of the
API spec for this event. Plugins should not be using that event, and
how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent
is undefined.
== AT ==
public net.minecraft.server.network.ServerLoginPacketListenerImpl$State
public net.minecraft.server.network.ServerLoginPacketListenerImpl state
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index c45b8b2c89ffec7bd6a6875963c61f11185d3ee1..38947f40864f3661df2eb4baa0aef5740b82f9d9 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -692,6 +692,26 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
packetlistener1.onDisconnect(disconnectiondetails);
}
this.pendingActions.clear(); // Free up packet queue.
+ // Paper start - Add PlayerConnectionCloseEvent
+ final PacketListener packetListener = this.getPacketListener();
+ if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
+ /* Player was logged in, either game listener or configuration listener */
+ final com.mojang.authlib.GameProfile profile = commonPacketListener.getOwner();
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(),
+ profile.getName(), ((InetSocketAddress) this.address).getAddress(), false).callEvent();
+ } else if (packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginListener) {
+ /* Player is login stage */
+ switch (loginListener.state) {
+ case VERIFYING:
+ case WAITING_FOR_DUPE_DISCONNECT:
+ case PROTOCOL_SWITCHING:
+ case ACCEPTED:
+ final com.mojang.authlib.GameProfile profile = loginListener.authenticatedProfile; /* Should be non-null at this stage */
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
+ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
+ }
+ }
+ // Paper end - Add PlayerConnectionCloseEvent
}
}
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 636b8aef2348fa4cfe63a9b7d77a64b14dc7a42c..de25b9ea8aa4b7d6fd3fed12cdd16be9ddfcbfff 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -86,7 +86,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
@Nullable
String requestedUsername;
@Nullable
- private GameProfile authenticatedProfile;
+ public GameProfile authenticatedProfile; // Paper - public
private final String serverId;
private final boolean transferred;
private ServerPlayer player; // CraftBukkit

View file

@ -1,164 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Wed, 2 Jan 2019 00:35:43 -0600
Subject: [PATCH] Replace OfflinePlayer#getLastPlayed
Currently OfflinePlayer#getLastPlayed could more accurately be described
as "OfflinePlayer#getLastTimeTheirDataWasSaved".
The API doc says it should return the last time the server "witnessed"
the player, whilst also saying it should return the last time they
logged in. The current implementation does neither.
Given this interesting contradiction in the API documentation and the
current defacto implementation, I've elected to deprecate (with no
intent to remove) and replace it with two new methods, clearly named and
documented as to their purpose.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index c2571855ca6a8ecd144b8fbb97d601d1808e8d61..bd130d3a4d0cfe431be627c3f4d85bb394fe099b 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -269,6 +269,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
private int containerCounter;
public boolean wonGame;
private int containerUpdateDelay; // Paper - Configurable container update tick rate
+ public long loginTime; // Paper - Replace OfflinePlayer#getLastPlayed
// Paper start - cancellable death event
public boolean queueHealthUpdatePacket;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 5fc76fd70f98fe874b38d8da08017fdadbd115e5..cdd1d8222ad1796abd0858b9ed0e2ddc9be83c93 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -182,6 +182,7 @@ public abstract class PlayerList {
public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie clientData) {
player.isRealPlayer = true; // Paper
+ player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
GameProfile gameprofile = player.getGameProfile();
GameProfileCache usercache = this.server.getProfileCache();
// Optional optional; // CraftBukkit - decompile error
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 461656e1cb095243bfe7a9ee2906e5b00574ae78..411b280ac3e27e72091db813c0c9b69b62df6097 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -262,6 +262,61 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
return this.getData() != null;
}
+ // Paper start
+ @Override
+ public long getLastLogin() {
+ Player player = getPlayer();
+ if (player != null) return player.getLastLogin();
+
+ CompoundTag data = getPaperData();
+
+ if (data != null) {
+ if (data.contains("LastLogin")) {
+ return data.getLong("LastLogin");
+ } else {
+ // if the player file cannot provide accurate data, this is probably the closest we can approximate
+ File file = getDataFile();
+ return file.lastModified();
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public long getLastSeen() {
+ Player player = getPlayer();
+ if (player != null) return player.getLastSeen();
+
+ CompoundTag data = getPaperData();
+
+ if (data != null) {
+ if (data.contains("LastSeen")) {
+ return data.getLong("LastSeen");
+ } else {
+ // if the player file cannot provide accurate data, this is probably the closest we can approximate
+ File file = getDataFile();
+ return file.lastModified();
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private CompoundTag getPaperData() {
+ CompoundTag result = getData();
+
+ if (result != null) {
+ if (!result.contains("Paper")) {
+ result.put("Paper", new CompoundTag());
+ }
+ result = result.getCompound("Paper");
+ }
+
+ return result;
+ }
+ // Paper end
+
@Override
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 004ac565d4124f6059d530034cf0c9a28f0be467..6a3bf3f34ff36e0b11bb3c250074f672b1e81b4f 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -210,6 +210,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener();
public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API
private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit
+ private long lastSaveTime; // Paper - getLastPlayed replacement API
public CraftPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
@@ -2049,6 +2050,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.firstPlayed = firstPlayed;
}
+ // Paper start - getLastPlayed replacement API
+ @Override
+ public long getLastLogin() {
+ return this.getHandle().loginTime;
+ }
+
+ @Override
+ public long getLastSeen() {
+ return this.isOnline() ? System.currentTimeMillis() : this.lastSaveTime;
+ }
+ // Paper end - getLastPlayed replacement API
+
public void readExtraData(CompoundTag nbttagcompound) {
this.hasPlayedBefore = true;
if (nbttagcompound.contains("bukkit")) {
@@ -2071,6 +2084,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void setExtraData(CompoundTag nbttagcompound) {
+ this.lastSaveTime = System.currentTimeMillis(); // Paper
+
if (!nbttagcompound.contains("bukkit")) {
nbttagcompound.put("bukkit", new CompoundTag());
}
@@ -2085,6 +2100,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
data.putLong("firstPlayed", this.getFirstPlayed());
data.putLong("lastPlayed", System.currentTimeMillis());
data.putString("lastKnownName", handle.getScoreboardName());
+
+ // Paper start - persist for use in offline save data
+ if (!nbttagcompound.contains("Paper")) {
+ nbttagcompound.put("Paper", new CompoundTag());
+ }
+
+ CompoundTag paper = nbttagcompound.getCompound("Paper");
+ paper.putLong("LastLogin", handle.loginTime);
+ paper.putLong("LastSeen", System.currentTimeMillis());
+ // Paper end
}
@Override

View file

@ -1,24 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: connorhartley <vectrixu+gh@gmail.com>
Date: Mon, 7 Jan 2019 14:43:48 -0600
Subject: [PATCH] Workaround for vehicle tracking issue on disconnect
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index bd130d3a4d0cfe431be627c3f4d85bb394fe099b..ad2b8ea068469f2b0597c0e0436ad3c94dcb62ea 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1833,6 +1833,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
public void disconnect() {
this.disconnected = true;
this.ejectPassengers();
+
+ // Paper start - Workaround vehicle not tracking the passenger disconnection dismount
+ if (this.isPassenger() && this.getVehicle() instanceof ServerPlayer) {
+ this.stopRiding();
+ }
+ // Paper end - Workaround vehicle not tracking the passenger disconnection dismount
+
if (this.isSleeping()) {
this.stopSleepInBed(true, false);
}

View file

@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 4 Feb 2019 23:33:24 -0500
Subject: [PATCH] Dont block Player#remove if the handle is a custom player
Upstream throws UOE if you try to call remove on a Player.
We just add a check to ensure that the CraftPlayer's handle
is a ServerPlayer
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 6a3bf3f34ff36e0b11bb3c250074f672b1e81b4f..d0af4b838bd43ef2388e918ac14e7acec71b63ef 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -224,8 +224,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void remove() {
+ if (this.getHandle().getClass().equals(ServerPlayer.class)) { // special case for NMS plugins inheriting
// Will lead to an inconsistent player state if we remove the player as any other entity.
throw new UnsupportedOperationException(String.format("Cannot remove player %s, use Player#kickPlayer(String) instead.", this.getName()));
+ } else {
+ super.remove();
+ }
}
@Override

View file

@ -1,53 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 6 Feb 2019 00:20:33 -0500
Subject: [PATCH] BlockDestroyEvent
Adds an event for when the server is going to destroy a current block,
potentially causing it to drop. This event can be cancelled to avoid
the block destruction, such as preventing signs from popping when
floating in the air.
This can replace many uses of BlockPhysicsEvent
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 5929b450a26e7c3cf63de3dc1d0e67cb781b24c7..4c4449f7dee8695a362f83b9356e5754244fff18 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -25,6 +25,7 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
+import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
@@ -576,9 +577,26 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
return false;
} else {
FluidState fluid = this.getFluidState(pos);
+ // Paper start - BlockDestroyEvent; while the above setAir method is named same and looks very similar
+ // they are NOT used with same intent and the above should not fire this event. The above method is more of a BlockSetToAirEvent,
+ // it doesn't imply destruction of a block that plays a sound effect / drops an item.
+ boolean playEffect = true;
+ BlockState effectType = iblockdata;
+ int xp = iblockdata.getBlock().getExpDrop(iblockdata, (ServerLevel) this, pos, ItemStack.EMPTY, true);
+ if (com.destroystokyo.paper.event.block.BlockDestroyEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluid.createLegacyBlock().createCraftBlockData(), effectType.createCraftBlockData(), xp, drop);
+ if (!event.callEvent()) {
+ return false;
+ }
+ effectType = ((CraftBlockData) event.getEffectBlock()).getState();
+ playEffect = event.playEffect();
+ drop = event.willDrop();
+ xp = event.getExpToDrop();
+ }
+ // Paper end - BlockDestroyEvent
- if (!(iblockdata.getBlock() instanceof BaseFireBlock)) {
- this.levelEvent(2001, pos, Block.getId(iblockdata));
+ if (playEffect && !(effectType.getBlock() instanceof BaseFireBlock)) { // Paper - BlockDestroyEvent
+ this.levelEvent(2001, pos, Block.getId(effectType)); // Paper - BlockDestroyEvent
}
if (drop) {

View file

@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Callahan <mr.callahhh@gmail.com>
Date: Wed, 8 Apr 2020 02:42:14 -0500
Subject: [PATCH] Async command map building
This adds a custom pool inorder to make sure that they are closed
without much though, as it doesn't matter if the client is not sent
commands if the server is restarting. Using the default async pool caused issues to arise
due to the shutdown logic generally being much later.
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 72756ef14b8ec8afd80313b9f6aaf76722cb18cf..a05aea8561ac102476ee1b3068942b095950a86a 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -450,6 +450,24 @@ public class Commands {
if ( org.spigotmc.SpigotConfig.tabComplete < 0 ) return; // Spigot
// CraftBukkit start
// Register Vanilla commands into builtRoot as before
+ // Paper start - Perf: Async command map building
+ COMMAND_SENDING_POOL.execute(() -> {
+ this.sendAsync(player);
+ });
+ }
+
+ public static final java.util.concurrent.ThreadPoolExecutor COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor(
+ 0, 2, 60L, java.util.concurrent.TimeUnit.SECONDS,
+ new java.util.concurrent.LinkedBlockingQueue<>(),
+ new com.google.common.util.concurrent.ThreadFactoryBuilder()
+ .setNameFormat("Paper Async Command Builder Thread Pool - %1$d")
+ .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER))
+ .build(),
+ new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy()
+ );
+
+ private void sendAsync(ServerPlayer player) {
+ // Paper end - Perf: Async command map building
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
RootCommandNode vanillaRoot = new RootCommandNode();
@@ -467,7 +485,14 @@ public class Commands {
for (CommandNode node : rootcommandnode.getChildren()) {
bukkit.add(node.getName());
}
+ // Paper start - Perf: Async command map building
+ net.minecraft.server.MinecraftServer.getServer().execute(() -> {
+ runSync(player, bukkit, rootcommandnode);
+ });
+ }
+ private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
+ // Paper end - Perf: Async command map building
PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
event.getPlayer().getServer().getPluginManager().callEvent(event);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 699191356a9d873fa6bbe04ea3e5db91eb2db41d..8ecfd7a3daa99dabe796d28d27790fb8b45c628b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -930,6 +930,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
MinecraftServer.LOGGER.info("Stopping server");
+ Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
MinecraftTimings.stopServer(); // Paper
// CraftBukkit start
if (this.server != null) {

View file

@ -1,206 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 19 Apr 2020 18:15:29 -0400
Subject: [PATCH] Brigadier Mojang API
Adds AsyncPlayerSendCommandsEvent
- Allows modifying on a per command basis what command data they see.
Adds CommandRegisteredEvent
- Allows manipulating the CommandNode to add more children/metadata for the client
diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
index 3370731ee064d2693b972a0765c13dd4fd69f66a..09d486a05179b9d878e1c33725b4e614c3544da9 100644
--- a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
+++ b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
@@ -5,7 +5,7 @@ package com.mojang.brigadier.exceptions;
import com.mojang.brigadier.Message;
-public class CommandSyntaxException extends Exception {
+public class CommandSyntaxException extends Exception implements net.kyori.adventure.util.ComponentMessageThrowable { // Paper - Brigadier API
public static final int CONTEXT_AMOUNT = 10;
public static boolean ENABLE_COMMAND_STACK_TRACES = true;
public static BuiltInExceptionProvider BUILT_IN_EXCEPTIONS = new BuiltInExceptions();
@@ -73,4 +73,11 @@ public class CommandSyntaxException extends Exception {
public int getCursor() {
return cursor;
}
+
+ // Paper start - Brigadier API
+ @Override
+ public @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component componentMessage() {
+ return io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(this.message);
+ }
+ // Paper end - Brigadier API
}
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
index da6250df1c5f3385b683cffde47754bca4606f5e..14ccd0c8f721e9be7dca8a5dcb8ef95b5cd82731 100644
--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
@@ -34,6 +34,7 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
private final RedirectModifier<S> modifier;
private final boolean forks;
private Command<S> command;
+ public CommandNode<CommandSourceStack> clientNode; // Paper - Brigadier API
// CraftBukkit start
public void removeCommand(String name) {
this.children.remove(name);
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index d9fc3c25bef251df6a53ee47ec224b07240a931c..2a22827f44dd0d524c22264447959a6979e9f0de 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -45,7 +45,7 @@ import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
-public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider {
+public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper - Brigadier API
public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
@@ -170,6 +170,26 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
return this.textName;
}
+ // Paper start - Brigadier API
+ @Override
+ public org.bukkit.entity.Entity getBukkitEntity() {
+ return getEntity() != null ? getEntity().getBukkitEntity() : null;
+ }
+
+ @Override
+ public org.bukkit.World getBukkitWorld() {
+ return getLevel() != null ? getLevel().getWorld() : null;
+ }
+
+ @Override
+ public org.bukkit.Location getBukkitLocation() {
+ Vec3 pos = getPosition();
+ org.bukkit.World world = getBukkitWorld();
+ Vec2 rot = getRotation();
+ return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z, rot != null ? rot.y : 0, rot != null ? rot.x : 0) : null;
+ }
+ // Paper end - Brigadier API
+
@Override
public boolean hasPermission(int level) {
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index a05aea8561ac102476ee1b3068942b095950a86a..e25fc35716aff1d1805884b18f67b0eb33d8c05c 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -486,6 +486,7 @@ public class Commands {
bukkit.add(node.getName());
}
// Paper start - Perf: Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper - Brigadier API
net.minecraft.server.MinecraftServer.getServer().execute(() -> {
runSync(player, bukkit, rootcommandnode);
});
@@ -493,6 +494,7 @@ public class Commands {
private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
// Paper end - Perf: Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, true).callEvent(); // Paper - Brigadier API
PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
event.getPlayer().getServer().getPluginManager().callEvent(event);
@@ -511,6 +513,11 @@ public class Commands {
while (iterator.hasNext()) {
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
+ // Paper start - Brigadier API
+ if (commandnode2.clientNode != null) {
+ commandnode2 = commandnode2.clientNode;
+ }
+ // Paper end - Brigadier API
if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot
if (commandnode2.canUse(source)) {
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 577f0e290f66afd2ded18714fb8b5f62a7a9aa71..e786d4b940a6fcd6d5ce66c5e13f52ff001b8367 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -769,19 +769,34 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
}
}
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
+ // Paper start - Brigadier API
+ com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, packet.getCommand());
+ suggestEvent.setCancelled(suggestions.isEmpty());
+ if (suggestEvent.callEvent()) {
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS)));
+ }
+ // Paper end - Brigadier API
}
}
+ // Paper start - brig API
+ private static Suggestions limitTo(final Suggestions suggestions, final int size) {
+ return suggestions.getList().size() <= size ? suggestions : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, size));
+ }
+ // Paper end - brig API
private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringreader) {
// Paper end - AsyncTabCompleteEvent
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
- Suggestions suggestions1 = suggestions.getList().size() <= 1000 ? suggestions : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, 1000));
-
- this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions1));
+ // Paper start - Brigadier API
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, packet.getCommand());
+ suggestEvent.setCancelled(suggestions.isEmpty());
+ if (suggestEvent.callEvent()) {
+ this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS)));
+ }
+ // Paper end - Brigadier API
});
}
diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
index 83d81b9371902b0302d13e53b31c15fac4e67966..d113e54a30db16e2ad955170df6030d15de530d6 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
@@ -20,7 +20,7 @@ import org.bukkit.command.CommandException;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
-public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack> {
+public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
private final CraftServer server;
private final Command command;
@@ -31,10 +31,24 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman
}
public LiteralCommandNode<CommandSourceStack> register(CommandDispatcher<CommandSourceStack> dispatcher, String label) {
- return dispatcher.register(
- LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this)
- .then(RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this))
- );
+ // Paper start - Expose Brigadier to Paper-MojangAPI
+ com.mojang.brigadier.tree.RootCommandNode<CommandSourceStack> root = dispatcher.getRoot();
+ LiteralCommandNode<CommandSourceStack> literal = LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this).build();
+ LiteralCommandNode<CommandSourceStack> defaultNode = literal;
+ com.mojang.brigadier.tree.ArgumentCommandNode<CommandSourceStack, String> defaultArgs = RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this).build();
+ literal.addChild(defaultArgs);
+ com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<CommandSourceStack> event = new com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<>(label, this, this.command, root, literal, defaultArgs);
+ if (!event.callEvent()) {
+ return null;
+ }
+ literal = event.getLiteral();
+ if (event.isRawCommand()) {
+ defaultNode.clientNode = literal;
+ literal = defaultNode;
+ }
+ root.addChild(literal);
+ return literal;
+ // Paper end
}
@Override

View file

@ -1,436 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 25 Jun 2023 23:10:14 -0700
Subject: [PATCH] Improve exact choice recipe ingredients
Fixes exact choices not working with recipe book clicks
and shapeless recipes.
== AT ==
public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG
public net.minecraft.world.entity.player.StackedContents put(II)V
public net.minecraft.world.entity.player.StackedContents take(II)I
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef68600f6b59674ddea6c77f7e412902888e39b7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java
@@ -0,0 +1,30 @@
+package io.papermc.paper.inventory.recipe;
+
+import net.minecraft.world.Container;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.Recipe;
+
+public abstract class RecipeBookExactChoiceRecipe<C extends net.minecraft.world.item.crafting.RecipeInput> implements Recipe<C> {
+
+ private boolean hasExactIngredients;
+
+ protected final void checkExactIngredients() {
+ // skip any special recipes
+ if (this.isSpecial()) {
+ this.hasExactIngredients = false;
+ return;
+ }
+ for (final Ingredient ingredient : this.getIngredients()) {
+ if (!ingredient.isEmpty() && ingredient.exact) {
+ this.hasExactIngredients = true;
+ return;
+ }
+ }
+ this.hasExactIngredients = false;
+ }
+
+ @Override
+ public final boolean hasExactIngredients() {
+ return this.hasExactIngredients;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..568ba6aed2e74b8d84f4e82c1e785ef1587e2617
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java
@@ -0,0 +1,109 @@
+package io.papermc.paper.inventory.recipe;
+
+import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntComparators;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.world.entity.player.StackedContents;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.ItemStackLinkedSet;
+import net.minecraft.world.item.crafting.CraftingInput;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.Recipe;
+
+public final class StackedContentsExtraMap {
+
+ private final AtomicInteger idCounter = new AtomicInteger(BuiltInRegistries.ITEM.size()); // start at max vanilla stacked contents idx
+ public final Object2IntMap<ItemStack> exactChoiceIds = new Object2IntOpenCustomHashMap<>(ItemStackLinkedSet.TYPE_AND_TAG);
+ private final Int2ObjectMap<ItemStack> idToExactChoice = new Int2ObjectOpenHashMap<>();
+ private final StackedContents contents;
+ public final Map<Ingredient, IntList> extraStackingIds = new IdentityHashMap<>();
+
+ public StackedContentsExtraMap(final StackedContents contents, final Recipe<?> recipe) {
+ this.exactChoiceIds.defaultReturnValue(-1);
+ this.contents = contents;
+ this.initialize(recipe);
+ }
+
+ private void initialize(final Recipe<?> recipe) {
+ if (recipe.hasExactIngredients()) {
+ for (final Ingredient ingredient : recipe.getIngredients()) {
+ if (!ingredient.isEmpty() && ingredient.exact) {
+ final net.minecraft.world.item.ItemStack[] items = ingredient.getItems();
+ final IntList idList = new IntArrayList(items.length);
+ for (final ItemStack item : items) {
+ idList.add(this.registerExact(item)); // I think not copying the stack here is safe because cb copies the stack when creating the ingredient
+ if (item.getComponentsPatch().isEmpty()) {
+ // add regular index if it's a plain itemstack but still registered as exact
+ idList.add(StackedContents.getStackingIndex(item));
+ }
+ }
+ idList.sort(IntComparators.NATURAL_COMPARATOR);
+ this.extraStackingIds.put(ingredient, idList);
+ }
+ }
+ }
+ }
+
+ private int registerExact(final ItemStack exactChoice) {
+ final int existing = this.exactChoiceIds.getInt(exactChoice);
+ if (existing > -1) {
+ return existing;
+ }
+ final int id = this.idCounter.getAndIncrement();
+ this.exactChoiceIds.put(exactChoice, id);
+ this.idToExactChoice.put(id, exactChoice);
+ return id;
+ }
+
+ public ItemStack getById(int id) {
+ return this.idToExactChoice.get(id);
+ }
+
+ public Int2IntMap regularRemoved = new Int2IntArrayMap();
+ public void accountInput(final CraftingInput input) {
+ // similar logic to the CraftingInput constructor
+ for (final ItemStack item : input.items()) {
+ if (!item.isEmpty()) {
+ if (this.accountStack(item, 1)) {
+ // remove one of the items if it was added to the contents as a non-extra item
+ final int plainStackIdx = StackedContents.getStackingIndex(item);
+ if (this.contents.take(plainStackIdx, 1) == plainStackIdx) {
+ this.regularRemoved.put(plainStackIdx, 1);
+ }
+ }
+ }
+ }
+ }
+
+ public void resetExtras() {
+ // clear previous extra ids
+ for (final int extraId : this.exactChoiceIds.values()) {
+ this.contents.contents.remove(extraId);
+ }
+ for (final Int2IntMap.Entry entry : this.regularRemoved.int2IntEntrySet()) {
+ this.contents.put(entry.getIntKey(), entry.getIntValue());
+ }
+ }
+
+ public boolean accountStack(final ItemStack stack, final int count) {
+ if (!this.exactChoiceIds.isEmpty()) {
+ final int id = this.exactChoiceIds.getInt(stack);
+ if (id >= 0) {
+ this.contents.put(id, count);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/package-info.java b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..413dfa52760db393ad6a8b5341200ee704a864fc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.inventory.recipe;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
index 0bd749af8014dd437229594ef6981a2ead803990..6d1f9c15dc99917a2ac966ea38ef1970f4f0289c 100644
--- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
+++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
@@ -31,6 +31,7 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple
this.inventory = entity.getInventory();
if (this.testClearGrid() || entity.isCreative()) {
this.stackedContents.clear();
+ this.stackedContents.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients
entity.getInventory().fillStackedContents(this.stackedContents);
this.menu.fillCraftSlotsStackedContents(this.stackedContents);
if (this.stackedContents.canCraft(recipe.value(), null)) {
@@ -77,7 +78,7 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple
int l = k;
for (int m : intList) {
- ItemStack itemStack2 = StackedContents.fromStackingIndex(m);
+ ItemStack itemStack2 = StackedContents.fromStackingIndexWithExtras(m, this.stackedContents); // Paper - Improve exact choice recipe ingredients
if (!itemStack2.isEmpty()) {
int n = itemStack2.getMaxStackSize();
if (n < l) {
@@ -96,12 +97,22 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple
@Override
public void addItemToSlot(Integer input, int slot, int amount, int gridX, int gridY) {
Slot slot2 = this.menu.getSlot(slot);
- ItemStack itemStack = StackedContents.fromStackingIndex(input);
+ // Paper start - Improve exact choice recipe ingredients
+ ItemStack itemStack = null;
+ boolean isExact = false;
+ if (this.stackedContents.extrasMap != null && input >= net.minecraft.core.registries.BuiltInRegistries.ITEM.size()) {
+ itemStack = StackedContents.fromStackingIndexExtras(input, this.stackedContents.extrasMap).copy();
+ isExact = true;
+ }
+ if (itemStack == null) {
+ itemStack = StackedContents.fromStackingIndex(input);
+ }
+ // Paper end - Improve exact choice recipe ingredients
if (!itemStack.isEmpty()) {
int i = amount;
while (i > 0) {
- i = this.moveItemToGrid(slot2, itemStack, i);
+ i = this.moveItemToGrid(slot2, itemStack, i, isExact); // Paper - Improve exact choice recipe ingredients
if (i == -1) {
return;
}
@@ -133,8 +144,15 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple
return i;
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Improve exact choice recipe ingredients
+
protected int moveItemToGrid(Slot slot, ItemStack stack, int i) {
- int j = this.inventory.findSlotMatchingUnusedItem(stack);
+ // Paper start - Improve exact choice recipe ingredients
+ return this.moveItemToGrid(slot, stack, i, false);
+ }
+ protected int moveItemToGrid(Slot slot, ItemStack stack, int i, final boolean isExact) {
+ int j = isExact ? this.inventory.findSlotMatchingItem(stack) : this.inventory.findSlotMatchingUnusedItem(stack);
+ // Paper end - Improve exact choice recipe ingredients
if (j == -1) {
return -1;
} else {
diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
index fa5576e41baec4b52c7ebb877924eb91d3775a2d..fcabf630ce1e4949d00f485a5bff66dd1e54a277 100644
--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java
+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
@@ -22,8 +22,10 @@ import net.minecraft.world.item.crafting.RecipeHolder;
public class StackedContents {
private static final int EMPTY = 0;
public final Int2IntMap contents = new Int2IntOpenHashMap();
+ @Nullable public io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap = null; // Paper - Improve exact choice recipe ingredients
public void accountSimpleStack(ItemStack stack) {
+ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; max of 64 due to accountStack method below
if (!stack.isDamaged() && !stack.isEnchanted() && !stack.has(DataComponents.CUSTOM_NAME)) {
this.accountStack(stack);
}
@@ -37,6 +39,7 @@ public class StackedContents {
if (!stack.isEmpty()) {
int i = getStackingIndex(stack);
int j = Math.min(maxCount, stack.getCount());
+ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, j)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it
this.put(i, j);
}
}
@@ -83,6 +86,31 @@ public class StackedContents {
return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId));
}
+ // Paper start - Improve exact choice recipe ingredients
+ public void initializeExtras(final Recipe<?> recipe, @Nullable final net.minecraft.world.item.crafting.CraftingInput input) {
+ this.extrasMap = new io.papermc.paper.inventory.recipe.StackedContentsExtraMap(this, recipe);
+ if (input != null) this.extrasMap.accountInput(input);
+ }
+
+ public void resetExtras() {
+ if (this.extrasMap != null && !this.contents.isEmpty()) {
+ this.extrasMap.resetExtras();
+ }
+ this.extrasMap = null;
+ }
+
+ public static ItemStack fromStackingIndexWithExtras(final int itemId, @Nullable final StackedContents contents) {
+ if (contents != null && contents.extrasMap != null && itemId >= BuiltInRegistries.ITEM.size()) {
+ return fromStackingIndexExtras(itemId, contents.extrasMap);
+ }
+ return fromStackingIndex(itemId);
+ }
+
+ public static ItemStack fromStackingIndexExtras(final int itemId, final io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap) {
+ return extrasMap.getById(itemId).copy();
+ }
+ // Paper end - Improve exact choice recipe ingredients
+
public void clear() {
this.contents.clear();
}
@@ -106,7 +134,7 @@ public class StackedContents {
this.data = new BitSet(this.ingredientCount + this.itemCount + this.ingredientCount + this.ingredientCount * this.itemCount);
for (int i = 0; i < this.ingredients.size(); i++) {
- IntList intList = this.ingredients.get(i).getStackingIds();
+ IntList intList = this.getStackingIds(this.ingredients.get(i)); // Paper - Improve exact choice recipe ingredients
for (int j = 0; j < this.itemCount; j++) {
if (intList.contains(this.items[j])) {
@@ -169,7 +197,7 @@ public class StackedContents {
IntCollection intCollection = new IntAVLTreeSet();
for (Ingredient ingredient : this.ingredients) {
- intCollection.addAll(ingredient.getStackingIds());
+ intCollection.addAll(this.getStackingIds(ingredient)); // Paper - Improve exact choice recipe ingredients
}
IntIterator intIterator = intCollection.iterator();
@@ -298,7 +326,7 @@ public class StackedContents {
for (Ingredient ingredient : this.ingredients) {
int j = 0;
- for (int k : ingredient.getStackingIds()) {
+ for (int k : this.getStackingIds(ingredient)) { // Paper - Improve exact choice recipe ingredients
j = Math.max(j, StackedContents.this.contents.get(k));
}
@@ -309,5 +337,17 @@ public class StackedContents {
return i;
}
+
+ // Paper start - Improve exact choice recipe ingredients
+ private IntList getStackingIds(final Ingredient ingredient) {
+ if (StackedContents.this.extrasMap != null) {
+ final IntList ids = StackedContents.this.extrasMap.extraStackingIds.get(ingredient);
+ if (ids != null) {
+ return ids;
+ }
+ }
+ return ingredient.getStackingIds();
+ }
+ // Paper end - Improve exact choice recipe ingredients
}
}
diff --git a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
index f3b6466089ee8be59747a16aac2cac84be30617d..45c80500201aabc1e8643427ebfb8818ab966750 100644
--- a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
@@ -5,7 +5,7 @@ import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
-public abstract class AbstractCookingRecipe implements Recipe<SingleRecipeInput> {
+public abstract class AbstractCookingRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<SingleRecipeInput> implements Recipe<SingleRecipeInput> { // Paper - improve exact recipe choices
protected final RecipeType<?> type;
protected final CookingBookCategory category;
protected final String group;
@@ -24,6 +24,7 @@ public abstract class AbstractCookingRecipe implements Recipe<SingleRecipeInput>
this.result = result;
this.experience = experience;
this.cookingTime = cookingTime;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
@Override
diff --git a/src/main/java/net/minecraft/world/item/crafting/Recipe.java b/src/main/java/net/minecraft/world/item/crafting/Recipe.java
index 3cab383e01c124349f3f96bcbcfe91356d51aa30..b57568d5e9c4c148a4b3c303c925a813fdd5dc67 100644
--- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java
@@ -73,4 +73,10 @@ public interface Recipe<T extends RecipeInput> {
}
org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit
+
+ // Paper start - improved exact choice recipes
+ default boolean hasExactIngredients() {
+ return false;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
index 59372daacd6fef45373c0557ccebb6ff5f16f174..63cf2b66f51df68aa3f6d98c69368ce454869d64 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
@@ -17,7 +17,7 @@ import org.bukkit.craftbukkit.inventory.CraftShapedRecipe;
import org.bukkit.inventory.RecipeChoice;
// CraftBukkit end
-public class ShapedRecipe implements CraftingRecipe {
+public class ShapedRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingInput> implements CraftingRecipe { // Paper - improve exact recipe choices
final ShapedRecipePattern pattern;
final ItemStack result;
@@ -31,6 +31,7 @@ public class ShapedRecipe implements CraftingRecipe {
this.pattern = raw;
this.result = result;
this.showNotification = showNotification;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
public ShapedRecipe(String group, CraftingBookCategory category, ShapedRecipePattern raw, ItemStack result) {
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..213ee4aa988dd4c2a5a7be99b1d13f67338e5209 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -19,7 +19,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe;
// CraftBukkit end
-public class ShapelessRecipe implements CraftingRecipe {
+public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingInput> implements CraftingRecipe { // Paper - improve exact recipe choices
final String group;
final CraftingBookCategory category;
@@ -31,6 +31,7 @@ public class ShapelessRecipe implements CraftingRecipe {
this.category = category;
this.result = result;
this.ingredients = ingredients;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
// CraftBukkit start
@@ -75,7 +76,18 @@ public class ShapelessRecipe implements CraftingRecipe {
}
public boolean matches(CraftingInput input, Level world) {
- return input.ingredientCount() != this.ingredients.size() ? false : (input.size() == 1 && this.ingredients.size() == 1 ? ((Ingredient) this.ingredients.getFirst()).test(input.getItem(0)) : input.stackedContents().canCraft(this, (IntList) null));
+ // Paper start - unwrap ternary & better exact choice recipes
+ if (input.ingredientCount() != this.ingredients.size()) {
+ return false;
+ }
+ if (input.size() == 1 && this.ingredients.size() == 1) {
+ return this.ingredients.getFirst().test(input.getItem(0));
+ }
+ input.stackedContents().initializeExtras(this, input); // setup stacked contents for this recipe
+ final boolean canCraft = input.stackedContents().canCraft(this, null);
+ input.stackedContents().resetExtras();
+ return canCraft;
+ // Paper end - unwrap ternary & better exact choice recipes
}
public ItemStack assemble(CraftingInput input, HolderLookup.Provider lookup) {