More work?

This commit is contained in:
Bjarne Koll 2024-10-22 22:38:26 +02:00
parent 152e82ceec
commit 9e92fed7a2
No known key found for this signature in database
GPG key ID: 27F6CCCF55D2EE62
18 changed files with 158 additions and 172 deletions

View file

@ -1,35 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 31 Dec 2016 21:44:50 -0500
Subject: [PATCH] PlayerTeleportEndGatewayEvent
Allows you to access the Gateway being used in a teleport event
Fix the offset used for player teleportation
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index f067b10e13f01e751fc4ebf088740c7d40afcb99..e13692373d0efba9402c35ae5cce615dce0a5e1e 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1192,11 +1192,22 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
ResourceKey<LevelStem> resourcekey = worldserver1.getTypeKey();
if (worldserver != null && worldserver.dimension() == worldserver1.dimension()) { // CraftBukkit
+ // Paper start - gateway-specific teleport event
+ if (this.portalProcess != null && this.portalProcess.isSamePortal(((net.minecraft.world.level.block.EndGatewayBlock) net.minecraft.world.level.block.Blocks.END_GATEWAY)) && this.serverLevel().getBlockEntity(this.portalProcess.getEntryPosition()) instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity theEndGatewayBlockEntity) {
+ Location to = CraftLocation.toBukkit(teleportTarget.pos(), this.serverLevel().getWorld(), teleportTarget.yRot(), teleportTarget.xRot());
+ final com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent event = new com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent(this.getBukkitEntity(), this.getBukkitEntity().getLocation(), to, new org.bukkit.craftbukkit.block.CraftEndGateway(to.getWorld(), theEndGatewayBlockEntity));
+ if (!event.callEvent() || event.getTo() == null) {
+ return null;
+ }
+ this.connection.teleport(event.getTo());
+ } else {
+ // Paper end - gateway-specific teleport event
boolean result = this.connection.teleport(teleportTarget.pos().x, teleportTarget.pos().y, teleportTarget.pos().z, teleportTarget.yRot(), teleportTarget.xRot(), teleportTarget.cause());
if (!result) {
return null;
}
// CraftBukkit end
+ } // Paper
this.connection.resetPosition();
teleportTarget.postDimensionTransition().onTransition(this);
return this;

View file

@ -1,82 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 7 Jan 2017 15:24:46 -0500
Subject: [PATCH] Provide E/TE/Chunk count stat methods
Provides counts without the ineffeciency of using .getEntities().size()
which creates copy of the collections.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 03b0720c6ebf1a876d56d18a941e0a06ed26dbf0..4fcbea7b8b12be10157da0c1f35c06e47c0334ad 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -114,7 +114,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public static final int TICKS_PER_DAY = 24000;
public static final int MAX_ENTITY_SPAWN_Y = 20000000;
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
- protected final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList();
+ public final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList(); // Paper - public
protected final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
private boolean tickingBlockEntities;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 4878a1b085a83dd4a8ffdc86250b8fb4fbac5192..1b7660a2d003bfb19ef80ba8066b6417f85328ec 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -164,6 +164,56 @@ public class CraftWorld extends CraftRegionAccessor implements World {
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftWorld.DATA_TYPE_REGISTRY);
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
+ // Paper start - Provide fast information methods
+ @Override
+ public int getEntityCount() {
+ int ret = 0;
+ for (net.minecraft.world.entity.Entity entity : world.getEntities().getAll()) {
+ if (entity.isChunkLoaded()) {
+ ++ret;
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public int getTileEntityCount() {
+ // We don't use the full world tile entity list, so we must iterate chunks
+ int size = 0;
+ for (ChunkHolder playerchunk : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.world)) {
+ net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk();
+ if (chunk == null) {
+ continue;
+ }
+ size += chunk.blockEntities.size();
+ }
+ return size;
+ }
+
+ @Override
+ public int getTickableTileEntityCount() {
+ return world.blockEntityTickers.size();
+ }
+
+ @Override
+ public int getChunkCount() {
+ int ret = 0;
+
+ for (ChunkHolder chunkHolder : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.world)) {
+ if (chunkHolder.getTickingChunk() != null) {
+ ++ret;
+ }
+ }
+
+ return ret;
+ }
+
+ @Override
+ public int getPlayerCount() {
+ return world.players().size();
+ }
+ // Paper end
+
private static final Random rand = new Random();
public CraftWorld(ServerLevel world, ChunkGenerator gen, BiomeProvider biomeProvider, Environment env) {

View file

@ -1,27 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 7 Jan 2017 15:41:58 -0500
Subject: [PATCH] Enforce Sync Player Saves
Saving players async is extremely dangerous. This will force it to main
the same way we handle async chunk loads.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 98862db2334508ee1a783aeabfb146751552a4d9..e17f4f3882ae77a67e8bfad04646f02c2bbb6669 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1029,11 +1029,13 @@ public abstract class PlayerList {
}
public void saveAll() {
+ io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
MinecraftTimings.savePlayers.startTiming(); // Paper
for (int i = 0; i < this.players.size(); ++i) {
- this.save((ServerPlayer) this.players.get(i));
+ this.save(this.players.get(i));
}
MinecraftTimings.savePlayers.stopTiming(); // Paper
+ return null; }); // Paper - ensure main
}
public UserWhiteList getWhiteList() {

View file

@ -1,354 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 19 Dec 2017 16:31:46 -0500
Subject: [PATCH] ExperienceOrbs API for Reason/Source/Triggering player
Adds lots of information about why this orb exists.
Replaces isFromBottle() with logic that persists entity reloads too.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index 99771070840a545537fe352bda1c4aaeedd638ca..2fab84c5e2dc4de39281956390588a9a71d02f68 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -435,7 +435,7 @@ public class ServerPlayerGameMode {
// Drop event experience
if (flag && event != null) {
- iblockdata.getBlock().popExperience(this.level, pos, event.getExpToDrop());
+ iblockdata.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper
}
return true;
diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
index 25a45e680f9fdea90f43d59de87a3a500f4ee8c0..0330a62a6a0060d2a96de191db68774588fc7ae5 100644
--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
@@ -44,9 +44,63 @@ public class ExperienceOrb extends Entity {
public int value;
public int count;
private Player followingPlayer;
+ // Paper start
+ @javax.annotation.Nullable
+ public java.util.UUID sourceEntityId;
+ @javax.annotation.Nullable
+ public java.util.UUID triggerEntityId;
+ public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
+
+ private void loadPaperNBT(CompoundTag tag) {
+ if (!tag.contains("Paper.ExpData", net.minecraft.nbt.Tag.TAG_COMPOUND)) {
+ return;
+ }
+ CompoundTag comp = tag.getCompound("Paper.ExpData");
+ if (comp.hasUUID("source")) {
+ this.sourceEntityId = comp.getUUID("source");
+ }
+ if (comp.hasUUID("trigger")) {
+ this.triggerEntityId = comp.getUUID("trigger");
+ }
+ if (comp.contains("reason")) {
+ String reason = comp.getString("reason");
+ try {
+ this.spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.valueOf(reason);
+ } catch (Exception e) {
+ this.level().getCraftServer().getLogger().warning("Invalid spawnReason set for experience orb: " + e.getMessage() + " - " + reason);
+ }
+ }
+ }
+ private void savePaperNBT(CompoundTag tag) {
+ CompoundTag comp = new CompoundTag();
+ if (this.sourceEntityId != null) {
+ comp.putUUID("source", this.sourceEntityId);
+ }
+ if (this.triggerEntityId != null) {
+ comp.putUUID("trigger", triggerEntityId);
+ }
+ if (this.spawnReason != null && this.spawnReason != org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN) {
+ comp.putString("reason", this.spawnReason.name());
+ }
+ tag.put("Paper.ExpData", comp);
+ }
+ @io.papermc.paper.annotation.DoNotUse
+ @Deprecated
public ExperienceOrb(Level world, double x, double y, double z, int amount) {
+ this(world, x, y, z, amount, null, null);
+ }
+
+ public ExperienceOrb(Level world, double x, double y, double z, int amount, @javax.annotation.Nullable org.bukkit.entity.ExperienceOrb.SpawnReason reason, @javax.annotation.Nullable Entity triggerId) {
+ this(world, x, y, z, amount, reason, triggerId, null);
+ }
+
+ public ExperienceOrb(Level world, double x, double y, double z, int amount, @javax.annotation.Nullable org.bukkit.entity.ExperienceOrb.SpawnReason reason, @javax.annotation.Nullable Entity triggerId, @javax.annotation.Nullable Entity sourceId) {
this(EntityType.EXPERIENCE_ORB, world);
+ this.sourceEntityId = sourceId != null ? sourceId.getUUID() : null;
+ this.triggerEntityId = triggerId != null ? triggerId.getUUID() : null;
+ this.spawnReason = reason != null ? reason : org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
+ // Paper end
this.setPos(x, y, z);
this.setYRot((float) (this.random.nextDouble() * 360.0D));
this.setDeltaMovement((this.random.nextDouble() * 0.20000000298023224D - 0.10000000149011612D) * 2.0D, this.random.nextDouble() * 0.2D * 2.0D, (this.random.nextDouble() * 0.20000000298023224D - 0.10000000149011612D) * 2.0D);
@@ -170,12 +224,20 @@ public class ExperienceOrb extends Entity {
}
public static void award(ServerLevel world, Vec3 pos, int amount) {
+ // Paper start - add reasons for orbs
+ award(world, pos, amount, null, null, null);
+ }
+ public static void award(ServerLevel world, Vec3 pos, int amount, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId) {
+ award(world, pos, amount, reason, triggerId, null);
+ }
+ public static void award(ServerLevel world, Vec3 pos, int amount, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId, Entity sourceId) {
+ // Paper end - add reasons for orbs
while (amount > 0) {
int j = ExperienceOrb.getExperienceValue(amount);
amount -= j;
if (!ExperienceOrb.tryMergeToExisting(world, pos, j)) {
- world.addFreshEntity(new ExperienceOrb(world, pos.x(), pos.y(), pos.z(), j));
+ world.addFreshEntity(new ExperienceOrb(world, pos.x(), pos.y(), pos.z(), j, reason, triggerId, sourceId)); // Paper - add reason
}
}
@@ -245,6 +307,7 @@ public class ExperienceOrb extends Entity {
nbt.putShort("Age", (short) this.age);
nbt.putShort("Value", (short) this.value);
nbt.putInt("Count", this.count);
+ this.savePaperNBT(nbt); // Paper
}
@Override
@@ -253,6 +316,7 @@ public class ExperienceOrb extends Entity {
this.age = nbt.getShort("Age");
this.value = nbt.getShort("Value");
this.count = Math.max(nbt.getInt("Count"), 1);
+ this.loadPaperNBT(nbt); // Paper
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 16d611e09cbe56525cde0e0dced93b7f09c12158..b6a7c9ee6e6885e0cc44e2f2ff3ea7bba9cb8f3d 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1799,7 +1799,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
protected void dropExperience(@Nullable Entity attacker) {
// CraftBukkit start - Update getExpReward() above if the removed if() changes!
if (!(this instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon)) { // CraftBukkit - SPIGOT-2420: Special case ender dragon will drop the xp over time
- ExperienceOrb.award((ServerLevel) this.level(), this.position(), this.expToDrop);
+ ExperienceOrb.award((ServerLevel) this.level(), this.position(), this.expToDrop, this instanceof ServerPlayer ? org.bukkit.entity.ExperienceOrb.SpawnReason.PLAYER_DEATH : org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, attacker, this); // Paper
this.expToDrop = 0;
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java
index 48d0cbe7859c62bbf281a7b43ef9af658667cb7b..b46352b328178df2a48d1c9e895bed3fabd2c292 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Animal.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java
@@ -251,12 +251,14 @@ public abstract class Animal extends AgeableMob {
public void finalizeSpawnChildFromBreeding(ServerLevel worldserver, Animal entityanimal, @Nullable AgeableMob entityageable, int experience) {
// CraftBukkit end
- Optional.ofNullable(this.getLoveCause()).or(() -> {
- return Optional.ofNullable(entityanimal.getLoveCause());
- }).ifPresent((entityplayer) -> {
+ // Paper start
+ ServerPlayer entityplayer = this.getLoveCause();
+ if (entityplayer == null) entityplayer = entityanimal.getLoveCause();
+ if (entityplayer != null) {
+ // Paper end
entityplayer.awardStat(Stats.ANIMALS_BRED);
CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer, this, entityanimal, entityageable);
- });
+ } // Paper
this.setAge(6000);
entityanimal.setAge(6000);
this.resetLove();
@@ -265,7 +267,7 @@ public abstract class Animal extends AgeableMob {
if (worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
// CraftBukkit start - use event experience
if (experience > 0) {
- worldserver.addFreshEntity(new ExperienceOrb(worldserver, this.getX(), this.getY(), this.getZ(), experience));
+ worldserver.addFreshEntity(new ExperienceOrb(worldserver, this.getX(), this.getY(), this.getZ(), experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer, entityageable)); // Paper
}
// CraftBukkit end
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
index de2a25db9465bc4ae3cbf7ff6d3af756df679f4a..a6788da1505f9e119c03b94488f5e006da13e918 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
@@ -897,7 +897,7 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
if (this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
// CraftBukkit start - use event experience
if (experience > 0) {
- this.level.addFreshEntity(new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), experience));
+ this.level.addFreshEntity(new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer, entityfox)); // Paper
}
// CraftBukkit end
}
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 d4659ce017692c6f8cabb56137a231bc566614b0..6f90ee749aed98b97868aa40fc233d164ddc2ef6 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
@@ -455,7 +455,7 @@ public class Turtle extends Animal {
RandomSource randomsource = this.animal.getRandom();
if (this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
- this.level.addFreshEntity(new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), randomsource.nextInt(7) + 1));
+ this.level.addFreshEntity(new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), randomsource.nextInt(7) + 1, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer)); // Paper;
}
}
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index 25a429a2d1725d562a28b9d07dba630cfe49d32a..f8283405b3c5bd43746a5738be55f600beea40e3 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -688,7 +688,7 @@ public class EnderDragon extends Mob implements Enemy {
if (this.level() instanceof ServerLevel) {
if (this.dragonDeathTime > 150 && this.dragonDeathTime % 5 == 0 && true) { // CraftBukkit - SPIGOT-2420: Already checked for the game rule when calculating the xp
- ExperienceOrb.award((ServerLevel) this.level(), this.position(), Mth.floor((float) short0 * 0.08F));
+ ExperienceOrb.award((ServerLevel) this.level(), this.position(), Mth.floor((float) short0 * 0.08F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, this.lastHurtByPlayer, this); // Paper
}
if (this.dragonDeathTime == 1 && !this.isSilent()) {
@@ -717,7 +717,7 @@ public class EnderDragon extends Mob implements Enemy {
this.move(MoverType.SELF, new Vec3(0.0D, 0.10000000149011612D, 0.0D));
if (this.dragonDeathTime == 200 && this.level() instanceof ServerLevel) {
if (true) { // CraftBukkit - SPIGOT-2420: Already checked for the game rule when calculating the xp
- ExperienceOrb.award((ServerLevel) this.level(), this.position(), Mth.floor((float) short0 * 0.2F));
+ ExperienceOrb.award((ServerLevel) this.level(), this.position(), Mth.floor((float) short0 * 0.2F), org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, this.lastHurtByPlayer, this); // Paper
}
if (this.dragonFight != null) {
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index 243eb1e54293c763a06febff551c051398d43535..79fdc8284f57a4f11e1954936ad574f7b8df5435 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -634,7 +634,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
}
if (offer.shouldRewardExp()) {
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
index 0f2e2e42e732e942664d70a72dd9c4e47c7e95b6..e51cb9c96e1bd13c00bf938436f4fc26d80055a1 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -207,7 +207,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
if (offer.shouldRewardExp()) {
int i = 3 + this.random.nextInt(4);
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
index ed43ad94ca007a54e3c32d5e17c141048eeb5835..0b4c67b9de6893601f032a8fae103e8a98f2c767 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
@@ -525,7 +525,7 @@ public class FishingHook extends Projectile {
this.level().addFreshEntity(entityitem);
// CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
if (playerFishEvent.getExpToDrop() > 0) {
- entityhuman.level().addFreshEntity(new ExperienceOrb(entityhuman.level(), entityhuman.getX(), entityhuman.getY() + 0.5D, entityhuman.getZ() + 0.5D, playerFishEvent.getExpToDrop()));
+ entityhuman.level().addFreshEntity(new ExperienceOrb(entityhuman.level(), entityhuman.getX(), entityhuman.getY() + 0.5D, entityhuman.getZ() + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.getPlayerOwner(), this)); // Paper
}
// CraftBukkit end
if (itemstack1.is(ItemTags.FISHES)) {
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java
index 9963db38420b91ae817a18ff084311cb45c0edee..70ceef96c6305324aef3b006f6603817ef187e9f 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java
@@ -54,7 +54,7 @@ public class ThrownExperienceBottle extends ThrowableItemProjectile {
}
// CraftBukkit end
- ExperienceOrb.award((ServerLevel) this.level(), this.position(), i);
+ ExperienceOrb.award((ServerLevel) this.level(), this.position(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, this.getOwner(), this); // Paper
this.discard(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
}
diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
index 1c0a9ca0ccfd89dd26736012ce1c329dffb7f913..637d77d6b07ff9ee5ac1cb0470cbefcba5c7495e 100644
--- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
@@ -98,7 +98,7 @@ public class GrindstoneMenu extends AbstractContainerMenu {
public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {
context.execute((world, blockposition) -> {
if (world instanceof ServerLevel) {
- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), this.getExperienceAmount(world));
+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), this.getExperienceAmount(world), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper
}
world.levelEvent(1042, blockposition, 0);
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
index def3e28edc206e0ba41111e26332db468223fb2e..6d0a90e9c637edff5c5ce1355a3b45f0fb7f4154 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -360,8 +360,13 @@ public class Block extends BlockBehaviour implements ItemLike {
}
public void popExperience(ServerLevel world, BlockPos pos, int size) {
+ // Paper start - add entity parameter
+ popExperience(world, pos, size, null);
+ }
+ public void popExperience(ServerLevel world, BlockPos pos, int size, net.minecraft.world.entity.Entity entity) {
+ // Paper end - add entity parameter
if (world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) {
- ExperienceOrb.award(world, Vec3.atCenterOf(pos), size);
+ ExperienceOrb.award(world, Vec3.atCenterOf(pos), size, org.bukkit.entity.ExperienceOrb.SpawnReason.BLOCK_BREAK, entity); // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
index c0dea02bb38dffb5003293c2d91e28e998da0a9c..e2a587ca5b732c62c4956e6f39ad795cd1411cc4 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
@@ -633,7 +633,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
j = event.getExpToDrop();
// CraftBukkit end
- ExperienceOrb.award(worldserver, vec3d, j);
+ ExperienceOrb.award(worldserver, vec3d, j, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, entityhuman); // Paper
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
index 5b7bdf06ae6df1eeccb5a8da143745235f58e07e..9b2b0bbb1755b6be1c23cf56e29a68b0002fd755 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
@@ -372,7 +372,7 @@ public final class CraftEntityTypes {
return item;
}));
register(new EntityTypeData<>(EntityType.EXPERIENCE_ORB, ExperienceOrb.class, CraftExperienceOrb::new,
- spawnData -> new net.minecraft.world.entity.ExperienceOrb(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), 0)
+ spawnData -> new net.minecraft.world.entity.ExperienceOrb(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), 0, org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM, null, null) // Paper
));
register(new EntityTypeData<>(EntityType.AREA_EFFECT_CLOUD, AreaEffectCloud.class, CraftAreaEffectCloud::new, spawnData -> new net.minecraft.world.entity.AreaEffectCloud(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
register(new EntityTypeData<>(EntityType.EGG, Egg.class, CraftEgg::new, spawnData -> new ThrownEgg(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z())));
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
index 9231511af4cba747594000364f0b8fceeeab4819..5a7d314ec0562e472f5dc45924a7b24841cff126 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
@@ -18,6 +18,18 @@ public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb {
this.getHandle().value = value;
}
+ // Paper start
+ public java.util.UUID getTriggerEntityId() {
+ return getHandle().triggerEntityId;
+ }
+ public java.util.UUID getSourceEntityId() {
+ return getHandle().sourceEntityId;
+ }
+ public SpawnReason getSpawnReason() {
+ return getHandle().spawnReason;
+ }
+ // Paper end
+
@Override
public net.minecraft.world.entity.ExperienceOrb getHandle() {
return (net.minecraft.world.entity.ExperienceOrb) this.entity;

View file

@ -1,44 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 22 Jan 2017 18:07:56 -0500
Subject: [PATCH] Cap Entity Collisions
Limit a single entity to colliding a max of configurable times per tick.
This will alleviate issues where living entities are hoarded in 1x1 pens
This is not tied to the maxEntityCramming rule. Cramming will still apply
just as it does in Vanilla, but entity pushing logic will be capped.
You can set this to 0 to disable collisions.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index bd17157631a74f80e3b5ce50bb1f681abe1dd6a7..46a21ed2408a42aafd16647e17e556730e799cbd 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -401,6 +401,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
public long activatedTick = Integer.MIN_VALUE;
public void inactiveTick() { }
// Spigot end
+ protected int numCollisions = 0; // Paper - Cap entity collisions
// Paper start - Entity origin API
@javax.annotation.Nullable
private org.bukkit.util.Vector origin;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index b6a7c9ee6e6885e0cc44e2f2ff3ea7bba9cb8f3d..f297b2227d0793f44cb5770aa24a474ec7283b15 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3502,10 +3502,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
Iterator iterator1 = list.iterator();
+ this.numCollisions = Math.max(0, this.numCollisions - this.level().paperConfig().collisions.maxEntityCollisions); // Paper - Cap entity collisions
- while (iterator1.hasNext()) {
+ while (iterator1.hasNext() && this.numCollisions < this.level().paperConfig().collisions.maxEntityCollisions) { // Paper - Cap entity collisions
Entity entity1 = (Entity) iterator1.next();
-
+ entity1.numCollisions++; // Paper - Cap entity collisions
+ this.numCollisions++; // Paper - Cap entity collisions
this.doPush(entity1);
}
}

View file

@ -1,48 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 5 Feb 2017 00:04:04 -0500
Subject: [PATCH] Remove CraftScheduler Async Task Debugger
I have not once ever seen this system help debug a crash.
One report of a suspected memory leak with the system.
This adds additional overhead to asynchronous task dispatching
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index cf9f04e005940f5dd7baf50435f3703fa7c2d4f0..f1145585eed18be0aa5c795a50589103fdc9cc2f 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -433,7 +433,7 @@ public class CraftScheduler implements BukkitScheduler {
}
this.parsePending();
} else {
- this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass()));
+ // this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper
this.executor.execute(new com.destroystokyo.paper.ServerSchedulerReportingWrapper(task)); // Paper
// We don't need to parse pending
// (async tasks must live with race-conditions if they attempt to cancel between these few lines of code)
@@ -450,7 +450,7 @@ public class CraftScheduler implements BukkitScheduler {
this.pending.addAll(temp);
temp.clear();
MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper
- this.debugHead = this.debugHead.getNextHead(currentTick);
+ //this.debugHead = this.debugHead.getNextHead(currentTick); // Paper
}
private void addTask(final CraftTask task) {
@@ -514,10 +514,15 @@ public class CraftScheduler implements BukkitScheduler {
@Override
public String toString() {
+ // Paper start
+ return "";
+ /*
int debugTick = this.currentTick;
StringBuilder string = new StringBuilder("Recent tasks from ").append(debugTick - CraftScheduler.RECENT_TICKS).append('-').append(debugTick).append('{');
this.debugHead.debugTo(string);
return string.append('}').toString();
+ */
+ // Paper end
}
@Deprecated

View file

@ -1,290 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Fri, 12 May 2017 23:34:11 -0500
Subject: [PATCH] Properly handle async calls to restart the server
The watchdog thread calls the server restart function asynchronously. Prior to
this change, it attempted to do several non-safe operations from the watchdog
thread, rather than the main. Specifically, because of a separate upstream change,
it causes player entities to be ticked asynchronously, among other things.
This is dangerous.
This patch moves the old handling into a synchronous variant, for calls from the
restart command, and adds separate handling for async calls, such as those from
the watchdog thread.
When calling from the watchdog thread, we cannot assume the main thread is in a
tickable state; it may be completely deadlocked. In order to handle this, we mark
the server as stopping, in order to account for situations where the server should
complete a tick reasonbly soon, i.e. 99% of cases.
Should the server not enter a state where it is stopping within 10 seconds, We
will assume that the server has in fact deadlocked and will proceed to force
kill the server.
This modification does not force restart the server should we actually enter a
deadlocked state where the server is stopping, whereas this will in most cases
exit within a reasonable amount of time, to put a fixed limit on a process that
will have plugins and worlds saving to the disk has a high potential to result
in corruption/dataloss.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 10d69b48158460e5739d1e41a83fcaeec819fac6..5d03477919ce892f6dfb4b2304c03733e10e3721 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -242,6 +242,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private Map<ResourceKey<Level>, ServerLevel> levels;
private PlayerList playerList;
private volatile boolean running;
+ private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
private boolean stopped;
private int tickCount;
private int ticksUntilAutosave;
@@ -937,7 +938,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.playerList != null) {
MinecraftServer.LOGGER.info("Saving players");
this.playerList.saveAll();
- this.playerList.removeAll();
+ this.playerList.removeAll(this.isRestarting); // Paper
try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
}
@@ -1017,6 +1018,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public void halt(boolean waitForShutdown) {
+ // Paper start - allow passing of the intent to restart
+ this.safeShutdown(waitForShutdown, false);
+ }
+ public void safeShutdown(boolean waitForShutdown, boolean isRestarting) {
+ this.isRestarting = isRestarting;
+ // Paper end
this.running = false;
if (waitForShutdown) {
try {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index e17f4f3882ae77a67e8bfad04646f02c2bbb6669..d5f17d7e3b56ca87ec9070b69265ce098de61f69 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1143,8 +1143,15 @@ public abstract class PlayerList {
}
public void removeAll() {
+ // Paper start - Extract method to allow for restarting flag
+ this.removeAll(false);
+ }
+
+ public void removeAll(boolean isRestarting) {
+ // Paper end
// CraftBukkit start - disconnect safely
for (ServerPlayer player : this.players) {
+ if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage)); else // Paper
player.connection.disconnect(java.util.Objects.requireNonNullElseGet(this.server.server.shutdownMessage(), net.kyori.adventure.text.Component::empty)); // CraftBukkit - add custom shutdown message // Paper - Adventure
}
// CraftBukkit end
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
index de8c703803bcd074f765a44cabc7c635176b716d..824c4ad135ea5177f416687c7042639ed126b70b 100644
--- a/src/main/java/org/spigotmc/RestartCommand.java
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -46,86 +46,134 @@ public class RestartCommand extends Command
AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
try
{
- String[] split = restartScript.split( " " );
- if ( split.length > 0 && new File( split[0] ).isFile() )
+ // Paper - extract method and cleanup
+ boolean isRestarting = addShutdownHook( restartScript );
+ if ( isRestarting )
{
- System.out.println( "Attempting to restart with " + restartScript );
+ System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
+ } else
+ {
+ System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
+ }
+ // Stop the watchdog
+ WatchdogThread.doStop();
- // Disable Watchdog
- WatchdogThread.doStop();
+ shutdownServer( isRestarting );
+ // Paper end
+ } catch ( Exception ex )
+ {
+ ex.printStackTrace();
+ }
+ }
- // Kick all players
- for ( ServerPlayer p : (List<ServerPlayer>) MinecraftServer.getServer().getPlayerList().players )
- {
- p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ) );
- }
- // Give the socket a chance to send the packets
- try
- {
- Thread.sleep( 100 );
- } catch ( InterruptedException ex )
- {
- }
- // Close the socket so we can rebind with the new process
- MinecraftServer.getServer().getConnection().stop();
+ // Paper start - sync copied from above with minor changes, async added
+ private static void shutdownServer(boolean isRestarting)
+ {
+ if ( MinecraftServer.getServer().isSameThread() )
+ {
+ // Kick all players
+ for ( ServerPlayer p : com.google.common.collect.ImmutableList.copyOf( MinecraftServer.getServer().getPlayerList().players ) )
+ {
+ p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ) );
+ }
+ // Give the socket a chance to send the packets
+ try
+ {
+ Thread.sleep( 100 );
+ } catch ( InterruptedException ex )
+ {
+ }
- // Give time for it to kick in
- try
- {
- Thread.sleep( 100 );
- } catch ( InterruptedException ex )
- {
- }
+ closeSocket();
- // Actually shutdown
- try
- {
- MinecraftServer.getServer().close();
- } catch ( Throwable t )
- {
- }
+ // Actually shutdown
+ try
+ {
+ MinecraftServer.getServer().close(); // calls stop()
+ } catch ( Throwable t )
+ {
+ }
+
+ // Actually stop the JVM
+ System.exit( 0 );
- // This will be done AFTER the server has completely halted
- Thread shutdownHook = new Thread()
+ } else
+ {
+ // Mark the server to shutdown at the end of the tick
+ MinecraftServer.getServer().safeShutdown( false, isRestarting );
+
+ // wait 10 seconds to see if we're actually going to try shutdown
+ try
+ {
+ Thread.sleep( 10000 );
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+
+ // Check if we've actually hit a state where the server is going to safely shutdown
+ // if we have, let the server stop as usual
+ if (MinecraftServer.getServer().isStopped()) return;
+
+ // If the server hasn't stopped by now, assume worse case and kill
+ closeSocket();
+ System.exit( 0 );
+ }
+ }
+ // Paper end
+
+ // Paper - Split from moved code
+ private static void closeSocket()
+ {
+ // Close the socket so we can rebind with the new process
+ MinecraftServer.getServer().getConnection().stop();
+
+ // Give time for it to kick in
+ try
+ {
+ Thread.sleep( 100 );
+ } catch ( InterruptedException ex )
+ {
+ }
+ }
+ // Paper end
+
+ // Paper start - copied from above and modified to return if the hook registered
+ private static boolean addShutdownHook(String restartScript)
+ {
+ String[] split = restartScript.split( " " );
+ if ( split.length > 0 && new File( split[0] ).isFile() )
+ {
+ Thread shutdownHook = new Thread()
+ {
+ @Override
+ public void run()
{
- @Override
- public void run()
+ try
{
- try
+ String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
+ if ( os.contains( "win" ) )
{
- String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
- if ( os.contains( "win" ) )
- {
- Runtime.getRuntime().exec( "cmd /c start " + restartScript );
- } else
- {
- Runtime.getRuntime().exec( "sh " + restartScript );
- }
- } catch ( Exception e )
+ Runtime.getRuntime().exec( "cmd /c start " + restartScript );
+ } else
{
- e.printStackTrace();
+ Runtime.getRuntime().exec( "sh " + restartScript );
}
+ } catch ( Exception e )
+ {
+ e.printStackTrace();
}
- };
-
- shutdownHook.setDaemon( true );
- Runtime.getRuntime().addShutdownHook( shutdownHook );
- } else
- {
- System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
-
- // Actually shutdown
- try
- {
- MinecraftServer.getServer().close();
- } catch ( Throwable t )
- {
}
- }
- System.exit( 0 );
- } catch ( Exception ex )
+ };
+
+ shutdownHook.setDaemon( true );
+ Runtime.getRuntime().addShutdownHook( shutdownHook );
+ return true;
+ } else
{
- ex.printStackTrace();
+ return false;
}
}
+ // Paper end
+
}

View file

@ -1,45 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Tue, 16 May 2017 21:29:08 -0500
Subject: [PATCH] Add option to make parrots stay on shoulders despite movement
Makes parrots not fall off whenever the player changes height, or touches water, or gets hit by a passing leaf.
Instead, switches the behavior so that players have to sneak to make the birds leave.
I suspect Mojang may switch to this behavior before full release.
To be converted into a Paper-API event at some point in the future?
== AT ==
public net.minecraft.world.entity.player.Player removeEntitiesOnShoulder()V
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 34aeb823950fac8eaef3f38b302c1585a45f7498..44cc60d92c6e83ecfa2c232b59986968d0161672 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2259,6 +2259,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
switch (packet.getAction()) {
case PRESS_SHIFT_KEY:
this.player.setShiftKeyDown(true);
+
+ // Paper start - Add option to make parrots stay
+ if (this.player.level().paperConfig().entities.behavior.parrotsAreUnaffectedByPlayerMovement) {
+ this.player.removeEntitiesOnShoulder();
+ }
+ // Paper end - Add option to make parrots stay
+
break;
case RELEASE_SHIFT_KEY:
this.player.setShiftKeyDown(false);
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 ba279ab6ec0ab41309607333b62a941e35dbf581..7995a4626b3ed68234d468418408b9a8e179b6f6 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -593,6 +593,7 @@ public abstract class Player extends LivingEntity {
this.playShoulderEntityAmbientSound(this.getShoulderEntityLeft());
this.playShoulderEntityAmbientSound(this.getShoulderEntityRight());
if (!this.level().isClientSide && (this.fallDistance > 0.5F || this.isInWater()) || this.abilities.flying || this.isSleeping() || this.isInPowderSnow) {
+ if (!this.level().paperConfig().entities.behavior.parrotsAreUnaffectedByPlayerMovement) // Paper - Add option to make parrots stay
this.removeEntitiesOnShoulder();
}

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Fri, 9 Jun 2017 07:24:34 -0700
Subject: [PATCH] Add configuration option to prevent player names from being
suggested
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 8e951ed126453cf1ffa81e5c8aa6e6ea5db03089..cfc59f1fb52dffb13fb214dd7c9cf71d40354ef0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2837,5 +2837,10 @@ public final class CraftServer implements Server {
commandMap.registerServerAliases();
return true;
}
+
+ @Override
+ public boolean suggestPlayerNamesWhenNullTabCompletions() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().commands.suggestPlayerNamesWhenNullTabCompletions;
+ }
// Paper end
}

View file

@ -1,20 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 11 Jun 2017 21:01:18 +0100
Subject: [PATCH] provide a configurable option to disable creeper lingering
effect spawns
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
index 976de2547f654bec58f53a4eff54df5176e815aa..39486215ac080220a6018a35a8437092dbc8fe9d 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
@@ -287,7 +287,7 @@ public class Creeper extends Monster implements PowerableMob {
private void spawnLingeringCloud() {
Collection<MobEffectInstance> collection = this.getActiveEffects();
- if (!collection.isEmpty()) {
+ if (!collection.isEmpty() && !this.level().paperConfig().entities.behavior.disableCreeperLingeringEffect) { // Paper - Option to disable creeper lingering effect
AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
entityareaeffectcloud.setOwner(this); // CraftBukkit

View file

@ -1,57 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Fri, 5 May 2017 03:57:17 -0500
Subject: [PATCH] Item#canEntityPickup
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index d8beadc96a7f779c39c8e22ffe52d872ac49a0ad..fe00b73b0aeac4e4c1496dcaf81595f57a4a073f 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -673,6 +673,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
ItemEntity entityitem = (ItemEntity) iterator.next();
if (!entityitem.isRemoved() && !entityitem.getItem().isEmpty() && !entityitem.hasPickUpDelay() && this.wantsToPickUp(entityitem.getItem())) {
+ // Paper start - Item#canEntityPickup
+ if (!entityitem.canMobPickup) {
+ continue;
+ }
+ // Paper end - Item#canEntityPickup
this.pickUpItem(entityitem);
}
}
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index c4cd7cf1567cdf57bfe9f2d80cd4a613efe10dc7..759c246540bbd5cb99c78a722c39b72fbc1951d4 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -60,6 +60,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
public UUID target;
public final float bobOffs;
private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit
+ public boolean canMobPickup = true; // Paper - Item#canEntityPickup
public ItemEntity(EntityType<? extends ItemEntity> type, Level world) {
super(type, world);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
index fb3738f4c558796f41e3327dd41b8aec68007a8a..5620a0849fda49313c68edfd747fedd09641a3d5 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
@@ -63,6 +63,18 @@ public class CraftItem extends CraftEntity implements Item {
}
}
+ // Paper start
+ @Override
+ public boolean canMobPickup() {
+ return this.getHandle().canMobPickup;
+ }
+
+ @Override
+ public void setCanMobPickup(boolean canMobPickup) {
+ this.getHandle().canMobPickup = canMobPickup;
+ }
+ // Paper end
+
@Override
public void setOwner(UUID uuid) {
this.getHandle().setTarget(uuid);

View file

@ -1,41 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sun, 7 May 2017 06:26:09 -0500
Subject: [PATCH] PlayerPickupItemEvent#setFlyAtPlayer
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index 759c246540bbd5cb99c78a722c39b72fbc1951d4..f51f04758e135294bab5c7d1f891a8d67fea78f5 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -436,6 +436,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
// CraftBukkit start - fire PlayerPickupItemEvent
int canHold = player.getInventory().canHold(itemstack);
int remaining = i - canHold;
+ boolean flyAtPlayer = false; // Paper
if (this.pickupDelay <= 0 && canHold > 0) {
itemstack.setCount(canHold);
@@ -443,8 +444,14 @@ public class ItemEntity extends Entity implements TraceableEntity {
PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems());
this.level().getCraftServer().getPluginManager().callEvent(playerEvent);
+ flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper
if (playerEvent.isCancelled()) {
itemstack.setCount(i); // SPIGOT-5294 - restore count
+ // Paper start
+ if (flyAtPlayer) {
+ player.take(this, i);
+ }
+ // Paper end
return;
}
@@ -474,6 +481,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
// CraftBukkit end
if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemstack)) {
+ if (flyAtPlayer) // Paper - PlayerPickupItemEvent
player.take(this, i);
if (itemstack.isEmpty()) {
this.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause

View file

@ -1,41 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sun, 11 Jun 2017 16:30:30 -0500
Subject: [PATCH] PlayerAttemptPickupItemEvent
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index f51f04758e135294bab5c7d1f891a8d67fea78f5..f9dfd6e7b610cfee75524a525ab0e72bed5522da 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -40,6 +40,7 @@ import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.EntityRemoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
// CraftBukkit end
+import org.bukkit.event.player.PlayerAttemptPickupItemEvent; // Paper
public class ItemEntity extends Entity implements TraceableEntity {
@@ -438,6 +439,22 @@ public class ItemEntity extends Entity implements TraceableEntity {
int remaining = i - canHold;
boolean flyAtPlayer = false; // Paper
+ // Paper start - PlayerAttemptPickupItemEvent
+ if (this.pickupDelay <= 0) {
+ PlayerAttemptPickupItemEvent attemptEvent = new PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+ this.level().getCraftServer().getPluginManager().callEvent(attemptEvent);
+
+ flyAtPlayer = attemptEvent.getFlyAtPlayer();
+ if (attemptEvent.isCancelled()) {
+ if (flyAtPlayer) {
+ player.take(this, i);
+ }
+
+ return;
+ }
+ }
+ // Paper end - PlayerAttemptPickupItemEvent
+
if (this.pickupDelay <= 0 && canHold > 0) {
itemstack.setCount(canHold);
// Call legacy event

View file

@ -1,77 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 8 Aug 2021 16:26:46 -0700
Subject: [PATCH] Do not submit profile lookups to worldgen threads
They block. On network I/O.
If enough tasks are submitted the server will eventually stall
out due to a sync load, as the worldgen threads will be
stalling on profile lookups.
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 396f368a7e21a7c7b1630b4e20cdbc452c4b0f84..54562fa04d14a937451ea7aa9d80194f2c31b471 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -92,6 +92,22 @@ public class Util {
private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main");
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
+ public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() {
+
+ private final AtomicInteger count = new AtomicInteger();
+
+ @Override
+ public Thread newThread(Runnable run) {
+ Thread ret = new Thread(run);
+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement());
+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
+ });
+ return ret;
+ }
+ });
+ // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
public static final int LINEAR_LOOKUP_THRESHOLD = 8;
private static final Set<String> ALLOWED_UNTRUSTED_LINK_PROTOCOLS = Set.of("http", "https");
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index aeb0c7ce9b6f93dadd407dbdefba053568f2e2fe..416b26c2ab62b29d640169166980e398d5824b14 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -169,7 +169,7 @@ public class GameProfileCache {
} else {
CompletableFuture<Optional<GameProfile>> completablefuture1 = CompletableFuture.supplyAsync(() -> {
return this.get(username);
- }, Util.backgroundExecutor()).whenCompleteAsync((optional, throwable) -> {
+ }, Util.PROFILE_EXECUTOR).whenCompleteAsync((optional, throwable) -> { // Paper - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
this.requests.remove(username);
}, this.executor);
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
index f720c5ef71edd65de76bcac9632173184576ea9e..c278caa030ceccec8e2721068848a34be513de88 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
@@ -105,7 +105,7 @@ public class SkullBlockEntity extends BlockEntity {
ProfileResult profileResult = apiServices.sessionService().fetchProfile(uuid, true);
return Optional.ofNullable(profileResult).map(ProfileResult::profile);
}
- }, Util.backgroundExecutor());
+ }, Util.PROFILE_EXECUTOR); // Paper - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
}
public static void clear() {
diff --git a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
index 56a71862eac5c45fde2e1d9e96e763d9dbf24cc4..0c250e5b0bd3232d829b0c028f237e6bf5fd334d 100644
--- a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
+++ b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
@@ -161,7 +161,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
@Override
public CompletableFuture<PlayerProfile> update() {
- return CompletableFuture.supplyAsync(this::getUpdatedProfile, Util.backgroundExecutor());
+ return CompletableFuture.supplyAsync(this::getUpdatedProfile, Util.PROFILE_EXECUTOR); // Paper - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
}
private CraftPlayerProfile getUpdatedProfile() {

View file

@ -1,767 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 15 Jan 2018 22:11:48 -0500
Subject: [PATCH] Basic PlayerProfile API
Establishes base extension of profile systems for future edits too
== AT ==
public org.bukkit.craftbukkit.profile.CraftProfileProperty
public org.bukkit.craftbukkit.profile.CraftPlayerTextures
public org.bukkit.craftbukkit.profile.CraftPlayerTextures copyFrom(Lorg/bukkit/profile/PlayerTextures;)V
public org.bukkit.craftbukkit.profile.CraftPlayerTextures rebuildPropertyIfDirty()V
public org.bukkit.craftbukkit.profile.CraftPlayerProfile toString(Lcom/mojang/authlib/properties/PropertyMap;)Ljava/lang/String;
public org.bukkit.craftbukkit.profile.CraftPlayerProfile getProperty(Ljava/lang/String;)Lcom/mojang/authlib/properties/Property;
public org.bukkit.craftbukkit.profile.CraftPlayerProfile setProperty(Ljava/lang/String;Lcom/mojang/authlib/properties/Property;)V
diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..8849862b45ccbbc635a1c316e9870bca81e55c04
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
@@ -0,0 +1,451 @@
+package com.destroystokyo.paper.profile;
+
+import com.google.common.base.Preconditions;
+import com.mojang.authlib.yggdrasil.ProfileResult;
+import io.papermc.paper.configuration.GlobalConfiguration;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Iterables;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import com.mojang.authlib.properties.PropertyMap;
+import net.minecraft.Util;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.players.GameProfileCache;
+import net.minecraft.util.StringUtil;
+import net.minecraft.world.item.component.ResolvableProfile;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.bukkit.configuration.serialization.SerializableAs;
+import org.bukkit.craftbukkit.configuration.ConfigSerializationUtil;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.profile.CraftPlayerTextures;
+import org.bukkit.craftbukkit.profile.CraftProfileProperty;
+import org.bukkit.profile.PlayerTextures;
+import org.jetbrains.annotations.NotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+@SerializableAs("PlayerProfile")
+public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
+
+ private boolean emptyName;
+ private boolean emptyUUID;
+ private GameProfile profile;
+ private final PropertySet properties = new PropertySet();
+
+ public CraftPlayerProfile(CraftPlayer player) {
+ this.profile = player.getHandle().getGameProfile();
+ }
+
+ public CraftPlayerProfile(UUID id, String name) {
+ this.profile = createAuthLibProfile(id, name);
+ this.emptyName = name == null;
+ this.emptyUUID = id == null;
+ }
+
+ public CraftPlayerProfile(GameProfile profile) {
+ Validate.notNull(profile, "GameProfile cannot be null!");
+ this.profile = profile;
+ }
+
+ public CraftPlayerProfile(ResolvableProfile resolvableProfile) {
+ this(resolvableProfile.id().orElse(null), resolvableProfile.name().orElse(null));
+ copyProfileProperties(resolvableProfile.gameProfile(), this.profile);
+ }
+
+ @Override
+ public boolean hasProperty(String property) {
+ return profile.getProperties().containsKey(property);
+ }
+
+ @Override
+ public void setProperty(ProfileProperty property) {
+ String name = property.getName();
+ PropertyMap properties = profile.getProperties();
+ properties.removeAll(name);
+
+ Preconditions.checkArgument(properties.size() < 16, "Cannot add more than 16 properties to a profile");
+ properties.put(name, new Property(name, property.getValue(), property.getSignature()));
+ }
+
+ @Override
+ public CraftPlayerTextures getTextures() {
+ return new CraftPlayerTextures(this);
+ }
+
+ @Override
+ public void setTextures(@Nullable PlayerTextures textures) {
+ if (textures == null) {
+ this.removeProperty("textures");
+ } else {
+ CraftPlayerTextures craftPlayerTextures = new CraftPlayerTextures(this);
+ craftPlayerTextures.copyFrom(textures);
+ craftPlayerTextures.rebuildPropertyIfDirty();
+ }
+ }
+
+ public GameProfile getGameProfile() {
+ return profile;
+ }
+
+ @Nullable
+ @Override
+ public UUID getId() {
+ return this.emptyUUID ? null : this.profile.getId();
+ }
+
+ @Override
+ @Deprecated(forRemoval = true)
+ public UUID setId(@Nullable UUID uuid) {
+ final GameProfile previousProfile = this.profile;
+ final UUID previousId = this.getId();
+ this.profile = createAuthLibProfile(uuid, previousProfile.getName());
+ copyProfileProperties(previousProfile, this.profile);
+ this.emptyUUID = uuid == null;
+ return previousId;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return getId();
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return this.emptyName ? null : this.profile.getName();
+ }
+
+ @Override
+ @Deprecated(forRemoval = true)
+ public String setName(@Nullable String name) {
+ GameProfile prev = this.profile;
+ this.profile = createAuthLibProfile(prev.getId(), name);
+ copyProfileProperties(prev, this.profile);
+ this.emptyName = name == null;
+ return prev.getName();
+ }
+
+ @Nonnull
+ @Override
+ public Set<ProfileProperty> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public void setProperties(Collection<ProfileProperty> properties) {
+ properties.forEach(this::setProperty);
+ }
+
+ @Override
+ public void clearProperties() {
+ profile.getProperties().clear();
+ }
+
+ @Override
+ public boolean removeProperty(String property) {
+ return !profile.getProperties().removeAll(property).isEmpty();
+ }
+
+ @Nullable
+ @Override
+ public Property getProperty(String property) {
+ return Iterables.getFirst(this.profile.getProperties().get(property), null);
+ }
+
+ @Nullable
+ @Override
+ public void setProperty(@NotNull String propertyName, @Nullable Property property) {
+ if (property != null) {
+ this.setProperty(new ProfileProperty(propertyName, property.value(), property.signature()));
+ } else {
+ profile.getProperties().removeAll(propertyName);
+ }
+ }
+
+ @Override
+ public @NotNull GameProfile buildGameProfile() {
+ GameProfile profile = new GameProfile(this.profile.getId(), this.profile.getName());
+ profile.getProperties().putAll(this.profile.getProperties());
+ return profile;
+ }
+
+ @Override
+ public @NotNull ResolvableProfile buildResolvableProfile() {
+ if (this.emptyName || this.emptyUUID) {
+ return new ResolvableProfile(this.emptyName ? Optional.empty() : Optional.of(this.profile.getName()), this.emptyUUID ? Optional.empty() : Optional.of(this.profile.getId()), this.profile.getProperties());
+ } else {
+ return new ResolvableProfile(this.buildGameProfile());
+ }
+ }
+
+ @Override
+ public CraftPlayerProfile clone() {
+ CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
+ clone.setProperties(getProperties());
+ return clone;
+ }
+
+ @Override
+ public boolean isComplete() {
+ return this.getId() != null && StringUtils.isNotBlank(this.getName());
+ }
+
+ @Override
+ public @NotNull CompletableFuture<PlayerProfile> update() {
+ return CompletableFuture.supplyAsync(() -> {
+ final CraftPlayerProfile clone = clone();
+ clone.complete(true);
+ return clone;
+ }, Util.PROFILE_EXECUTOR);
+ }
+
+ @Override
+ public boolean completeFromCache() {
+ return completeFromCache(false, GlobalConfiguration.get().proxies.isProxyOnlineMode());
+ }
+
+ public boolean completeFromCache(boolean onlineMode) {
+ return completeFromCache(false, onlineMode);
+ }
+
+ public boolean completeFromCache(boolean lookupUUID, boolean onlineMode) {
+ MinecraftServer server = MinecraftServer.getServer();
+ String name = profile.getName();
+ GameProfileCache userCache = server.getProfileCache();
+ if (this.getId() == null) {
+ final GameProfile profile;
+ if (onlineMode) {
+ profile = lookupUUID ? userCache.get(name).orElse(null) : userCache.getProfileIfCached(name);
+ } else {
+ // Make an OfflinePlayer using an offline mode UUID since the name has no profile
+ profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
+ }
+ if (profile != null) {
+ // if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
+ copyProfileProperties(this.profile, profile);
+ this.profile = profile;
+ this.emptyUUID = false; // UUID was just retrieved from user cache and profile isn't null (so a completed profile was found)
+ }
+ }
+
+ if ((profile.getName().isEmpty() || !hasTextures()) && this.getId() != null) {
+ Optional<GameProfile> optProfile = userCache.get(this.profile.getId());
+ if (optProfile.isPresent()) {
+ GameProfile profile = optProfile.get();
+ if (this.profile.getName().isEmpty()) {
+ // if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
+ copyProfileProperties(this.profile, profile);
+ this.profile = profile;
+ this.emptyName = false; // Name was just retrieved via the userCache
+ } else if (profile != this.profile) {
+ copyProfileProperties(profile, this.profile);
+ }
+ }
+ }
+ return this.isComplete();
+ }
+
+ public boolean complete(boolean textures) {
+ return complete(textures, GlobalConfiguration.get().proxies.isProxyOnlineMode());
+ }
+
+ public boolean complete(boolean textures, boolean onlineMode) {
+ if (this.isComplete() && (!textures || hasTextures())) { // Don't do lookup if we already have everything
+ return true;
+ }
+
+ MinecraftServer server = MinecraftServer.getServer();
+ boolean isCompleteFromCache = this.completeFromCache(true, onlineMode);
+ if (onlineMode && (!isCompleteFromCache || (textures && !hasTextures()))) {
+ ProfileResult result = server.getSessionService().fetchProfile(this.profile.getId(), true);
+ if (result != null && result.profile() != null) {
+ copyProfileProperties(result.profile(), this.profile, true);
+ }
+ if (this.isComplete()) {
+ server.getProfileCache().add(this.profile);
+ }
+ }
+ return this.isComplete() && (!onlineMode || !textures || hasTextures());
+ }
+
+ private static void copyProfileProperties(GameProfile source, GameProfile target) {
+ copyProfileProperties(source, target, false);
+ }
+
+ private static void copyProfileProperties(GameProfile source, GameProfile target, boolean clearTarget) {
+ if (source == target) {
+ throw new IllegalArgumentException("Source and target profiles are the same (" + source + ")");
+ }
+ PropertyMap sourceProperties = source.getProperties();
+ PropertyMap targetProperties = target.getProperties();
+ if (clearTarget) targetProperties.clear();
+ if (sourceProperties.isEmpty()) {
+ return;
+ }
+
+ for (Property property : sourceProperties.values()) {
+ targetProperties.removeAll(property.name());
+ targetProperties.put(property.name(), property);
+ }
+ }
+
+ private static GameProfile createAuthLibProfile(UUID uniqueId, String name) {
+ Preconditions.checkArgument(name == null || name.length() <= 16, "Name cannot be longer than 16 characters");
+ Preconditions.checkArgument(name == null || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name);
+ return new GameProfile(
+ uniqueId != null ? uniqueId : Util.NIL_UUID,
+ name != null ? name : ""
+ );
+ }
+
+ private static ProfileProperty toBukkit(Property property) {
+ return new ProfileProperty(property.name(), property.value(), property.signature());
+ }
+
+ public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
+ CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
+ copyProfileProperties(gameProfile, profile.profile);
+ return profile;
+ }
+
+ public static PlayerProfile asBukkitMirror(GameProfile profile) {
+ return new CraftPlayerProfile(profile);
+ }
+
+ public static Property asAuthlib(ProfileProperty property) {
+ return new Property(property.getName(), property.getValue(), property.getSignature());
+ }
+
+ public static GameProfile asAuthlibCopy(PlayerProfile profile) {
+ CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
+ return asAuthlib(craft.clone());
+ }
+
+ public static GameProfile asAuthlib(PlayerProfile profile) {
+ CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
+ return craft.getGameProfile();
+ }
+
+ public static ResolvableProfile asResolvableProfileCopy(PlayerProfile profile) {
+ return ((SharedPlayerProfile) profile).buildResolvableProfile();
+ }
+
+ @Override
+ public @NotNull Map<String, Object> serialize() {
+ Map<String, Object> map = new LinkedHashMap<>();
+ if (!this.emptyUUID) {
+ map.put("uniqueId", this.getId().toString());
+ }
+ if (!this.emptyName) {
+ map.put("name", getName());
+ }
+ if (!this.properties.isEmpty()) {
+ List<Object> propertiesData = new ArrayList<>();
+ for (ProfileProperty property : properties) {
+ propertiesData.add(CraftProfileProperty.serialize(new Property(property.getName(), property.getValue(), property.getSignature())));
+ }
+ map.put("properties", propertiesData);
+ }
+ return map;
+ }
+
+ public static CraftPlayerProfile deserialize(Map<String, Object> map) {
+ UUID uniqueId = ConfigSerializationUtil.getUuid(map, "uniqueId", true);
+ String name = ConfigSerializationUtil.getString(map, "name", true);
+
+ // This also validates the deserialized unique id and name (ensures that not both are null):
+ CraftPlayerProfile profile = new CraftPlayerProfile(uniqueId, name);
+
+ if (map.containsKey("properties")) {
+ for (Object propertyData : (List<?>) map.get("properties")) {
+ if (!(propertyData instanceof Map)) {
+ throw new IllegalArgumentException("Property data (" + propertyData + ") is not a valid Map");
+ }
+ Property property = CraftProfileProperty.deserialize((Map<?, ?>) propertyData);
+ profile.profile.getProperties().put(property.name(), property);
+ }
+ }
+
+ return profile;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || this.getClass() != o.getClass()) return false;
+ final CraftPlayerProfile that = (CraftPlayerProfile) o;
+ return this.emptyName == that.emptyName && this.emptyUUID == that.emptyUUID && Objects.equals(this.profile, that.profile);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.emptyName, this.emptyUUID, this.profile);
+ }
+
+ @Override
+ public String toString() {
+ return "CraftPlayerProfile [uniqueId=" + getId() +
+ ", name=" + getName() +
+ ", properties=" + org.bukkit.craftbukkit.profile.CraftPlayerProfile.toString(this.profile.getProperties()) +
+ "]";
+ }
+
+ private class PropertySet extends AbstractSet<ProfileProperty> {
+
+ @Override
+ @Nonnull
+ public Iterator<ProfileProperty> iterator() {
+ return new ProfilePropertyIterator(profile.getProperties().values().iterator());
+ }
+
+ @Override
+ public int size() {
+ return profile.getProperties().size();
+ }
+
+ @Override
+ public boolean add(ProfileProperty property) {
+ setProperty(property);
+ return true;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends ProfileProperty> c) {
+ //noinspection unchecked
+ setProperties((Collection<ProfileProperty>) c);
+ return true;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName());
+ }
+
+ private class ProfilePropertyIterator implements Iterator<ProfileProperty> {
+ private final Iterator<Property> iterator;
+
+ ProfilePropertyIterator(Iterator<Property> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public ProfileProperty next() {
+ return toBukkit(iterator.next());
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..48e774677edf17d4a99ae9ed23d1b371dab41abb
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
@@ -0,0 +1,30 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Environment;
+import com.mojang.authlib.EnvironmentParser;
+import com.mojang.authlib.GameProfileRepository;
+import com.mojang.authlib.minecraft.MinecraftSessionService;
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
+import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
+
+import java.net.Proxy;
+
+public class PaperAuthenticationService extends YggdrasilAuthenticationService {
+
+ private final Environment environment;
+
+ public PaperAuthenticationService(Proxy proxy) {
+ super(proxy);
+ this.environment = EnvironmentParser.getEnvironmentFromProperties().orElse(YggdrasilEnvironment.PROD.getEnvironment());
+ }
+
+ @Override
+ public MinecraftSessionService createMinecraftSessionService() {
+ return new PaperMinecraftSessionService(this.getServicesKeySet(), this.getProxy(), this.environment);
+ }
+
+ @Override
+ public GameProfileRepository createProfileRepository() {
+ return new PaperGameProfileRepository(this.getProxy(), this.environment);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b9e797b42c88b17d6a7c590a423f4e85d99a59d
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Environment;
+import com.mojang.authlib.ProfileLookupCallback;
+import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
+import java.net.Proxy;
+
+public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
+ public PaperGameProfileRepository(Proxy proxy, Environment environment) {
+ super(proxy, environment);
+ }
+
+ @Override
+ public void findProfilesByNames(String[] names, ProfileLookupCallback callback) {
+ super.findProfilesByNames(names, callback);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
new file mode 100644
index 0000000000000000000000000000000000000000..985e6fc43a0946943847e0c283426242ef594a26
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
@@ -0,0 +1,22 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Environment;
+import com.mojang.authlib.yggdrasil.ProfileResult;
+import com.mojang.authlib.yggdrasil.ServicesKeySet;
+import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
+
+import java.net.Proxy;
+import java.util.UUID;
+import org.jetbrains.annotations.Nullable;
+
+public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
+
+ protected PaperMinecraftSessionService(ServicesKeySet servicesKeySet, Proxy proxy, Environment environment) {
+ super(servicesKeySet, proxy, environment);
+ }
+
+ @Override
+ public @Nullable ProfileResult fetchProfile(final UUID profileId, final boolean requireSecure) {
+ return super.fetchProfile(profileId, requireSecure);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/SharedPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/SharedPlayerProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..332700f84c5587e47a4d2056bfbb5413de97e9f2
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/SharedPlayerProfile.java
@@ -0,0 +1,26 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import net.minecraft.world.item.component.ResolvableProfile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+public interface SharedPlayerProfile {
+
+ @Nullable UUID getUniqueId();
+
+ @Nullable String getName();
+
+ boolean removeProperty(@NotNull String property);
+
+ @Nullable Property getProperty(@NotNull String propertyName);
+
+ @Nullable void setProperty(@NotNull String propertyName, @Nullable Property property);
+
+ @NotNull GameProfile buildGameProfile();
+
+ @NotNull ResolvableProfile buildResolvableProfile();
+}
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index 6afede80c10503a261d0f735c351d943597be9ff..993296f9c2457809bd6b844c309895f417eb42a5 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
@@ -168,7 +168,7 @@ public class Main {
}
File file = (File) optionset.valueOf("universe"); // CraftBukkit
- Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file, optionset); // Paper - pass OptionSet to load paper config files
+ Services services = Services.create(new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY), file, optionset); // Paper - pass OptionSet to load paper config files; override authentication service
// CraftBukkit start
String s = (String) Optional.ofNullable((String) optionset.valueOf("world")).orElse(dedicatedserversettings.getProperties().levelName);
LevelStorageSource convertable = LevelStorageSource.createDefault(file.toPath());
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index 416b26c2ab62b29d640169166980e398d5824b14..774d81c702edb76a2f6184d4dc53687de6734a79 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -126,6 +126,17 @@ public class GameProfileCache {
return this.operationCount.incrementAndGet();
}
+ // Paper start
+ public @Nullable GameProfile getProfileIfCached(String name) {
+ GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
+ if (entry == null) {
+ return null;
+ }
+ entry.setLastAccess(this.getNextOperation());
+ return entry.getProfile();
+ }
+ // Paper end
+
public Optional<GameProfile> get(String name) {
String s1 = name.toLowerCase(Locale.ROOT);
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index cfc59f1fb52dffb13fb214dd7c9cf71d40354ef0..f2b2a352cdc0301601ffd4b4fb7a99304ba526e5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -262,6 +262,9 @@ import org.yaml.snakeyaml.error.MarkedYAMLException;
import net.md_5.bungee.api.chat.BaseComponent; // Spigot
+import javax.annotation.Nullable; // Paper
+import javax.annotation.Nonnull; // Paper
+
public final class CraftServer implements Server {
private final String serverName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper
private final String serverVersion;
@@ -307,6 +310,7 @@ public final class CraftServer implements Server {
static {
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
ConfigurationSerialization.registerClass(CraftPlayerProfile.class);
+ ConfigurationSerialization.registerClass(com.destroystokyo.paper.profile.CraftPlayerProfile.class); // Paper
CraftItemFactory.instance();
CraftEntityFactory.instance();
}
@@ -2842,5 +2846,39 @@ public final class CraftServer implements Server {
public boolean suggestPlayerNamesWhenNullTabCompletions() {
return io.papermc.paper.configuration.GlobalConfiguration.get().commands.suggestPlayerNamesWhenNullTabCompletions;
}
+
+ @Override
+ public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) {
+ return createProfile(uuid, null);
+ }
+
+ @Override
+ public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull String name) {
+ return createProfile(null, name);
+ }
+
+ @Override
+ public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
+ Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null);
+ if (player != null) return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer) player);
+
+ return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
+ }
+
+ @Override
+ public com.destroystokyo.paper.profile.PlayerProfile createProfileExact(@Nullable UUID uuid, @Nullable String name) {
+ Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null);
+ if (player == null) {
+ return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
+ }
+
+ if (java.util.Objects.equals(uuid, player.getUniqueId()) && java.util.Objects.equals(name, player.getName())) {
+ return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer) player);
+ }
+
+ final com.destroystokyo.paper.profile.CraftPlayerProfile profile = new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
+ profile.getGameProfile().getProperties().putAll(((CraftPlayer) player).getHandle().getGameProfile().getProperties());
+ return profile;
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
index 210fdad5f2041368fc359b275f1cbf05b70b6989..d9cc76d7e60001957c0f59fdc32d016f3589aa08 100644
--- a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
+++ b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
@@ -31,7 +31,7 @@ import org.bukkit.profile.PlayerTextures;
import org.jetbrains.annotations.ApiStatus;
@SerializableAs("PlayerProfile")
-public final class CraftPlayerProfile implements PlayerProfile {
+public final class CraftPlayerProfile implements PlayerProfile, com.destroystokyo.paper.profile.SharedPlayerProfile { // Paper
@Nonnull
public static GameProfile validateSkullProfile(@Nonnull GameProfile gameProfile) {
@@ -132,8 +132,10 @@ public final class CraftPlayerProfile implements PlayerProfile {
}
}
- void removeProperty(String propertyName) {
- this.properties.removeAll(propertyName);
+ // Paper start - change return value for shared interface
+ public boolean removeProperty(String propertyName) {
+ return !this.properties.removeAll(propertyName).isEmpty();
+ // Paper end
}
void rebuildDirtyProperties() {
@@ -283,6 +285,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
@Override
public Map<String, Object> serialize() {
+ // Paper - diff on change
Map<String, Object> map = new LinkedHashMap<>();
if (this.uniqueId != null) {
map.put("uniqueId", this.uniqueId.toString());
@@ -296,10 +299,12 @@ public final class CraftPlayerProfile implements PlayerProfile {
this.properties.forEach((propertyName, property) -> propertiesData.add(CraftProfileProperty.serialize(property)));
map.put("properties", propertiesData);
}
+ // Paper - diff on change
return map;
}
public static CraftPlayerProfile deserialize(Map<String, Object> map) {
+ // Paper - diff on change
UUID uniqueId = ConfigSerializationUtil.getUuid(map, "uniqueId", true);
String name = ConfigSerializationUtil.getString(map, "name", true);
@@ -313,7 +318,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
profile.properties.put(property.name(), property);
}
}
-
+ // Paper - diff on change
return profile;
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerTextures.java b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerTextures.java
index 78e47fec27f4dd955632d03f7181682af0429961..0dce455fb47b3f5a2eb2b15a1cdbc4c6a54b7b69 100644
--- a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerTextures.java
+++ b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerTextures.java
@@ -48,7 +48,7 @@ public final class CraftPlayerTextures implements PlayerTextures {
}
}
- private final CraftPlayerProfile profile;
+ private final com.destroystokyo.paper.profile.SharedPlayerProfile profile; // Paper
// The textures data is loaded lazily:
private boolean loaded = false;
@@ -67,7 +67,7 @@ public final class CraftPlayerTextures implements PlayerTextures {
// GameProfiles (even if these modifications are later reverted).
private boolean dirty = false;
- CraftPlayerTextures(@Nonnull CraftPlayerProfile profile) {
+ public CraftPlayerTextures(@Nonnull com.destroystokyo.paper.profile.SharedPlayerProfile profile) { // Paper
this.profile = profile;
}

View file

@ -1,98 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sweepyoface <github@sweepy.pw>
Date: Sat, 17 Jun 2017 18:48:21 -0400
Subject: [PATCH] Add UnknownCommandEvent
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index ec34e402104d7a696ea95e0b11ee70189b678ab9..d9fc3c25bef251df6a53ee47ec224b07240a931c 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -330,8 +330,13 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
}
public void sendFailure(Component message) {
+ // Paper start - Add UnknownCommandEvent
+ this.sendFailure(message, true);
+ }
+ public void sendFailure(Component message, boolean withStyle) {
+ // Paper end - Add UnknownCommandEvent
if (this.source.acceptsFailure() && !this.silent) {
- this.source.sendSystemMessage(Component.empty().append(message).withStyle(ChatFormatting.RED));
+ this.source.sendSystemMessage(withStyle ? Component.empty().append(message).withStyle(ChatFormatting.RED) : message); // Paper - Add UnknownCommandEvent
}
}
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index f94c0106b44d614483184e372c01c1504cb886b0..72756ef14b8ec8afd80313b9f6aaf76722cb18cf 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -329,7 +329,7 @@ public class Commands {
commandlistenerwrapper.getServer().getProfiler().push(() -> {
return "/" + s;
});
- ContextChain<CommandSourceStack> contextchain = Commands.finishParsing(parseresults, s, commandlistenerwrapper, label); // CraftBukkit
+ ContextChain contextchain = this.finishParsing(parseresults, s, commandlistenerwrapper, label); // CraftBukkit // Paper - Add UnknownCommandEvent
try {
if (contextchain != null) {
@@ -363,14 +363,18 @@ public class Commands {
}
@Nullable
- private static ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseresults, String s, CommandSourceStack commandlistenerwrapper, String label) { // CraftBukkit
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseresults, String s, CommandSourceStack commandlistenerwrapper, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent
try {
Commands.validateParseResults(parseresults);
return (ContextChain) ContextChain.tryFlatten(parseresults.getContext().build(s)).orElseThrow(() -> {
return CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseresults.getReader());
});
} catch (CommandSyntaxException commandsyntaxexception) {
- commandlistenerwrapper.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage()));
+ // Paper start - Add UnknownCommandEvent
+ final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text();
+ // commandlistenerwrapper.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage()));
+ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(commandsyntaxexception.getRawMessage()));
+ // Paper end - Add UnknownCommandEvent
if (commandsyntaxexception.getInput() != null && commandsyntaxexception.getCursor() >= 0) {
int i = Math.min(commandsyntaxexception.getInput().length(), commandsyntaxexception.getCursor());
MutableComponent ichatmutablecomponent = Component.empty().withStyle(ChatFormatting.GRAY).withStyle((chatmodifier) -> {
@@ -389,7 +393,17 @@ public class Commands {
}
ichatmutablecomponent.append((Component) Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
- commandlistenerwrapper.sendFailure(ichatmutablecomponent);
+ // Paper start - Add UnknownCommandEvent
+ // commandlistenerwrapper.sendFailure(ichatmutablecomponent);
+ builder
+ .append(net.kyori.adventure.text.Component.newline())
+ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent));
+ }
+ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(commandlistenerwrapper.getBukkitSender(), s, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build());
+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event);
+ if (event.message() != null) {
+ commandlistenerwrapper.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false);
+ // Paper end - Add UnknownCommandEvent
}
return null;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index f2b2a352cdc0301601ffd4b4fb7a99304ba526e5..31f87ce0865dd7560cd9ba634855a1defdc80df2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -948,7 +948,13 @@ public final class CraftServer implements Server {
// Spigot start
if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
- sender.sendMessage(org.spigotmc.SpigotConfig.unknownCommandMessage);
+ // Paper start
+ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandLine, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage));
+ this.getPluginManager().callEvent(event);
+ if (event.message() != null) {
+ sender.sendMessage(event.message());
+ }
+ // Paper end
}
// Spigot end

View file

@ -1,97 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 17 Jun 2017 15:18:30 -0400
Subject: [PATCH] Shoulder Entities Release API
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 7995a4626b3ed68234d468418408b9a8e179b6f6..362d75522aaa721e9fb1d12a149d0efec1ac17b1 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1995,20 +1995,45 @@ public abstract class Player extends LivingEntity {
}
+ // Paper start
+ public Entity releaseLeftShoulderEntity() {
+ Entity entity = this.respawnEntityOnShoulder0(this.getShoulderEntityLeft());
+ if (entity != null) {
+ this.setShoulderEntityLeft(new CompoundTag());
+ }
+ return entity;
+ }
+
+ public Entity releaseRightShoulderEntity() {
+ Entity entity = this.respawnEntityOnShoulder0(this.getShoulderEntityRight());
+ if (entity != null) {
+ this.setShoulderEntityRight(new CompoundTag());
+ }
+ return entity;
+ }
+ // Paper - maintain old signature
+
private boolean respawnEntityOnShoulder(CompoundTag nbttagcompound) { // CraftBukkit void->boolean
- if (!this.level().isClientSide && !nbttagcompound.isEmpty()) {
+ return this.respawnEntityOnShoulder0(nbttagcompound) != null;
+ }
+
+ // Paper - return entity
+ private Entity respawnEntityOnShoulder0(CompoundTag nbttagcompound) { // CraftBukkit void->boolean
+ if (!this.level().isClientSide && nbttagcompound != null && !nbttagcompound.isEmpty()) {
return EntityType.create(nbttagcompound, this.level()).map((entity) -> { // CraftBukkit
if (entity instanceof TamableAnimal) {
((TamableAnimal) entity).setOwnerUUID(this.uuid);
}
entity.setPos(this.getX(), this.getY() + 0.699999988079071D, this.getZ());
- return ((ServerLevel) this.level()).addWithUUID(entity, CreatureSpawnEvent.SpawnReason.SHOULDER_ENTITY); // CraftBukkit
- }).orElse(true); // CraftBukkit
+ boolean addedToWorld = ((ServerLevel) this.level()).addWithUUID(entity, CreatureSpawnEvent.SpawnReason.SHOULDER_ENTITY); // CraftBukkit
+ return addedToWorld ? entity : null;
+ }).orElse(null); // CraftBukkit // Paper - true -> null
}
- return true; // CraftBukkit
+ return null; // Paper - return null
}
+ // Paper end
@Override
public abstract boolean isSpectator();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
index a93895a6e656c25e819354ecf5c73ff4bae83675..f0efea03165039525a98dc30c34d876972d9fe71 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
@@ -520,6 +520,32 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
this.getHandle().getCooldowns().addCooldown(CraftItemType.bukkitToMinecraft(material), ticks);
}
+ // Paper start
+ @Override
+ public org.bukkit.entity.Entity releaseLeftShoulderEntity() {
+ if (!getHandle().getShoulderEntityLeft().isEmpty()) {
+ Entity entity = getHandle().releaseLeftShoulderEntity();
+ if (entity != null) {
+ return entity.getBukkitEntity();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public org.bukkit.entity.Entity releaseRightShoulderEntity() {
+ if (!getHandle().getShoulderEntityRight().isEmpty()) {
+ Entity entity = getHandle().releaseRightShoulderEntity();
+ if (entity != null) {
+ return entity.getBukkitEntity();
+ }
+ }
+
+ return null;
+ }
+ // Paper end
+
@Override
public boolean discoverRecipe(NamespacedKey recipe) {
return this.discoverRecipes(Arrays.asList(recipe)) != 0;