This commit is contained in:
Bjarne Koll 2024-10-24 23:03:27 +02:00
parent 35e01d7a80
commit 7616ebccd8
No known key found for this signature in database
GPG key ID: 27F6CCCF55D2EE62
11 changed files with 115 additions and 115 deletions

View file

@ -1,64 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 6 May 2020 05:00:57 -0400
Subject: [PATCH] Handle Oversized block entities in chunks
Splits out Extra Packets if too many TE's are encountered to prevent
creating too large of a packet to sed.
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index 1e75cd33c32f0e2923681da64b9b73b279933c1b..0a8d07bf68b0ceabd13c70196d357fce79dcc2c3 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -27,6 +27,14 @@ public class ClientboundLevelChunkPacketData {
private final CompoundTag heightmaps;
private final byte[] buffer;
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
+ // Paper start - Handle oversized block entities in chunks
+ private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
+
+ public List<net.minecraft.network.protocol.Packet<?>> getExtraPackets() {
+ return this.extraPackets;
+ }
+ // Paper end - Handle oversized block entities in chunks
// Paper start - Anti-Xray - Add chunk packet info
@Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); }
@@ -50,8 +58,18 @@ public class ClientboundLevelChunkPacketData {
extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
// Paper end
this.blockEntitiesData = Lists.newArrayList();
+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
+ // Paper start - Handle oversized block entities in chunks
+ if (++totalTileEntities > TE_LIMIT) {
+ var packet = entry2.getValue().getUpdatePacket();
+ if (packet != null) {
+ this.extraPackets.add(packet);
+ continue;
+ }
+ }
+ // Paper end - Handle oversized block entities in chunks
this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue()));
}
}
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 8ead66c134688b11dca15f6509147e726f182e6a..cfcac0fdc130120cb1f8d97c6353d93db7ddf81b 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -83,4 +83,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
public ClientboundLightUpdatePacketData getLightData() {
return this.lightData;
}
+
+ // Paper start - Handle oversized block entities in chunks
+ @Override
+ public java.util.List<Packet<?>> getExtraPackets() {
+ return this.chunkData.getExtraPackets();
+ }
+ // Paper end - Handle oversized block entities in chunks
}

View file

@ -1,68 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 2 Aug 2021 10:10:40 +0200
Subject: [PATCH] Check distance in entity interactions
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index 42d7ecfab6f72517904451d9df3f0404b176fdb2..0e38a641d8e537750166b56c57aca4a90d418af1 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -125,6 +125,7 @@ public class Util {
.filter(fileSystemProvider -> fileSystemProvider.getScheme().equalsIgnoreCase("jar"))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No jar file system provider found"));
+ public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions
private static Consumer<String> thePauser = message -> {
};
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 340c4452c09a98bc0220e6fe68dc65afc946986b..05dae689ada8d96009e81aabf95a626bae90ecd3 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1437,7 +1437,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (!source.is(DamageTypeTags.IS_PROJECTILE)) {
Entity entity = source.getDirectEntity();
- if (entity instanceof LivingEntity) {
+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions
LivingEntity entityliving = (LivingEntity) entity;
this.blockUsingShield(entityliving);
@@ -1557,6 +1557,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
d0 = source.getSourcePosition().x() - this.getX();
d1 = source.getSourcePosition().z() - this.getZ();
}
+ // Paper start - Check distance in entity interactions; see for loop in knockback method
+ if (Math.abs(d0) > 200) {
+ d0 = Math.random() - Math.random();
+ }
+ if (Math.abs(d1) > 200) {
+ d1 = Math.random() - Math.random();
+ }
+ // Paper end - Check distance in entity interactions
this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
if (!flag) {
@@ -2351,7 +2359,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
Entity entity = damagesource.getDirectEntity();
- if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency
+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions
this.blockUsingShield((LivingEntity) entity);
}
}
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
index 907f751c859855484151fb5d607acee2f2a35076..f1955afc8e367f80ead85bd5ad3b8d66c255565a 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
@@ -718,7 +718,7 @@ public class Boat extends VehicleEntity implements Leashable, VariantHolder<Boat
double d2 = (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D;
if (this.level().noCollision(this, this.getBoundingBox().move(0.0D, d2 - this.getY(), 0.0D))) {
- this.setPos(this.getX(), d2, this.getZ());
+ this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Check distance in entity interactions // TODO Still needed?
this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
this.lastYd = 0.0D;
}

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Sat, 15 Jun 2024 22:01:39 -0400
Subject: [PATCH] Configurable Sand Duping
diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
index caf9273cfafbbaaf8dc95498927383e608773665..b83be9bbb9f348da83c0fd1ecc7f65c8a58b45b9 100644
--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -419,7 +419,7 @@ public class FallingBlockEntity extends Entity {
boolean flag = (resourcekey1 == Level.END || resourcekey == Level.END) && resourcekey1 != resourcekey;
Entity entity = super.changeDimension(teleportTarget);
- this.forceTickAfterTeleportToDuplicate = entity != null && flag;
+ this.forceTickAfterTeleportToDuplicate = entity != null && flag && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper
return entity;
}
}

View file

@ -1,273 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Wed, 7 Dec 2022 17:25:19 -0500
Subject: [PATCH] Properly resend entities
This resolves some issues which caused entities to not be resent correctly.
Entities that are interacted with need to be resent to the client, so we resend all the entity
data to the player whilst making sure not to clear dirty entries from the tracker. This makes
sure that values will be correctly updated to other players.
This also adds utilities to aid in further preventing entity desyncs.
This also also fixes the bug causing cancelling PlayerInteractEvent to cause items to continue
to be used despite being cancelled on the server.
For example, items being consumed but never finishing, shields being put up, etc.
The underlying issue of this is that the client modifies their synced data values,
and so we have to (forcibly) resend them in order for the client to reset their using item state.
See: https://github.com/PaperMC/Paper/pull/1896
== AT ==
public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity
diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
index 02bf2705ca1c99023a83a22d92e1962181102297..0f99733660f91280e4c6262cf75b3c9cae86f65a 100644
--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
@@ -50,7 +50,7 @@ public class SynchedEntityData {
}
}
- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
+ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
}
@@ -151,6 +151,20 @@ public class SynchedEntityData {
}
}
+ // Paper start
+ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
+ // Because these values can possibly be desynced on the client.
+ @Nullable
+ public List<SynchedEntityData.DataValue<?>> packAll() {
+ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
+ for (final DataItem<?> dataItem : this.itemsById) {
+ list.add(dataItem.value());
+ }
+
+ return list;
+ }
+ // Paper end
+
public static class DataItem<T> {
final EntityDataAccessor<T> accessor;
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index e9dcdb1e09e84a9b451034ff4bdfa6eae2dd1c04..24b1715397ba8e6f5e9841a030d0e3d964356f89 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -561,6 +561,7 @@ public class ServerPlayerGameMode {
}
// Paper end - extend Player Interact cancellation
player.getBukkitEntity().updateInventory(); // SPIGOT-2867
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
@@ -612,6 +613,11 @@ public class ServerPlayerGameMode {
return enuminteractionresult;
} else {
+ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
+ if (this.interactResult && this.interactResult != cancelledItem) {
+ this.player.resyncUsingItem(this.player);
+ }
+ // Paper end - Properly cancel usable items
return InteractionResult.PASS;
}
}
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0034483685ba626e5883b857de96ffd36e57e150..4c04eb531b6989f7e618d201ecaa84298eab52c4 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1948,6 +1948,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
if (cancelled) {
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
return;
}
@@ -2717,7 +2718,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
- entity.getBukkitEntity().update(ServerGamePacketListenerImpl.this.player);
+ entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it
ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 3a90dd1289d393426151d4457edaf99731cc34db..ec058eb6a10500831f173dcb47576c32c7516318 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -393,7 +393,7 @@ public abstract class PlayerList {
((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
// CraftBukkit end
- player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
this.sendLevelInfo(player, worldserver1);
@@ -948,12 +948,17 @@ public abstract class PlayerList {
}
public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl networkHandler) {
+ // Paper start - collect packets
+ this.sendActiveEffects(entity, networkHandler::send);
+ }
+ public void sendActiveEffects(LivingEntity entity, java.util.function.Consumer<Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packetConsumer) {
+ // Paper end - collect packets
Iterator iterator = entity.getActiveEffects().iterator();
while (iterator.hasNext()) {
MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
- networkHandler.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false));
+ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false)); // Paper - collect packets
}
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 52f3d0d4ce28cc6566166ae9a5a1b236ff8c027d..74231cb1c89079473d1727aa3ae2a539d4250317 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -684,13 +684,45 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// CraftBukkit start
public void refreshEntityData(ServerPlayer to) {
- List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
+ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
- if (list != null) {
+ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
}
}
// CraftBukkit end
+ // Paper start
+ // This method should only be used if the data of an entity could have become desynced
+ // due to interactions on the client.
+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
+ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
+ if (tracker == null) {
+ return;
+ }
+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
+ serverEntity.sendPairingData(player, list::add);
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
+ }
+ }
+
+ // This method allows you to specifically resend certain data accessor keys to the client
+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
+ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
+ return;
+ }
+
+ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
+ for (final EntityDataAccessor<?> key : keys) {
+ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
+ values.add(synchedValue.value());
+ }
+
+ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values));
+ }
+ // Paper end
public boolean equals(Object object) {
return object instanceof Entity ? ((Entity) object).id == this.id : false;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 05dae689ada8d96009e81aabf95a626bae90ecd3..3b15995efc65a351da8dac009b9698494771fefb 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3879,6 +3879,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
return ((Byte) this.entityData.get(LivingEntity.DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
}
+ // Paper start - Properly cancel usable items
+ public void resyncUsingItem(ServerPlayer serverPlayer) {
+ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
+ }
+ // Paper end - Properly cancel usable items
private void updatingUsingItem() {
if (this.isUsingItem()) {
if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
index b586116d8cca1585f9c9e618ed4d0cb2ef2747be..acf38ef6d8de8b15cf2b09eb7bda390c4e446e9a 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
@@ -108,8 +108,7 @@ public interface Bucketable {
itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket());
if (playerBucketFishEvent.isCancelled()) {
((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
- entity.getBukkitEntity().update((ServerPlayer) player); // We need to play out these packets as the client assumes the fish is gone
- entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client
+ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper
return Optional.of(InteractionResult.FAIL);
}
entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
diff --git a/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java b/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
index 5b448006debecab983167d15fac59fc2a04805df..9523db353df026f33d7e859788612b97542bd001 100644
--- a/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
+++ b/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
@@ -58,10 +58,14 @@ public class SuspiciousStewItem extends Item {
public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {
SuspiciousStewEffects suspicioussteweffects = (SuspiciousStewEffects) itemstack.getOrDefault(DataComponents.SUSPICIOUS_STEW_EFFECTS, SuspiciousStewEffects.EMPTY);
+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packets = new java.util.ArrayList<>(); // Paper - bundlize packets
for (SuspiciousStewEffects.Entry suspicioussteweffects_a : suspicioussteweffects.effects()) {
- entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect()));
+ packets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect())); // Paper - bundlize packets
}
- entityplayer.server.getPlayerList().sendActivePlayerEffects(entityplayer);
+ // Paper start - bundlize packets
+ entityplayer.server.getPlayerList().sendActiveEffects(entityplayer, packets::add);
+ entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(packets));
+ // Paper end - bundlize packets
}
// CraftBukkit end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 9ca1fee03bfa557f1df7388c6043c9ec6d02a79a..cd789c235acf740ec29c30b180e7fbe1a140caa9 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1012,7 +1012,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return;
}
- entityTracker.broadcast(this.getHandle().getAddEntityPacket(entityTracker.serverEntity));
+ // Paper start - resend possibly desynced entity instead of add entity packet
+ for (final ServerPlayerConnection connection : entityTracker.seenBy) {
+ this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer());
+ }
+ // Paper end - resend possibly desynced entity instead of add entity packet
}
public void update(ServerPlayer player) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
index f3a9b3380246fb2dd4b60a8d1a94c5dfed98d316..350ad61ab3fe66abd528e353b431a4a6dac17506 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
@@ -39,9 +39,11 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
protected void update() {
super.update();
+ // Paper start, don't mark as dirty as this is handled in super.update()
// mark dirty, so that the client gets updated with item and rotation
- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
+ // Paper end
// update redstone
if (!this.getHandle().generation) {

File diff suppressed because it is too large Load diff

View file

@ -1,484 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <lynxplay101@gmail.com>
Date: Thu, 13 Jun 2024 23:45:32 +0200
Subject: [PATCH] Add registry entry and builders
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
index 5cf598905ed6a7ac2b0d9ced3420adaf20ceb6af..12220f78ffaf06433ada72fd0c7f22b97d55287d 100644
--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
@@ -1,6 +1,8 @@
package io.papermc.paper.registry;
import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
+import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntry;
import io.papermc.paper.registry.tag.TagKey;
import java.util.Collections;
@@ -70,7 +72,7 @@ public final class PaperRegistries {
static {
REGISTRY_ENTRIES = List.of(
// built-ins
- entry(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new),
+ writable(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new, PaperGameEventRegistryEntry.PaperBuilder::new),
entry(Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new),
entry(Registries.INSTRUMENT, RegistryKey.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::new),
entry(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new),
@@ -89,7 +91,7 @@ public final class PaperRegistries {
entry(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN, TrimPattern.class, CraftTrimPattern::new).delayed(),
entry(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE, DamageType.class, CraftDamageType::new).delayed(),
entry(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT, Wolf.Variant.class, CraftWolf.CraftVariant::new).delayed(),
- entry(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
+ writable(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new, PaperEnchantmentRegistryEntry.PaperBuilder::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(),
entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(),
diff --git a/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..481f5f0cfae1fada3bc3f873fb7e04c3086ea9bf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
@@ -0,0 +1,234 @@
+package io.papermc.paper.registry.data;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.data.util.Checks;
+import io.papermc.paper.registry.data.util.Conversions;
+import io.papermc.paper.registry.set.PaperRegistrySets;
+import io.papermc.paper.registry.set.RegistryKeySet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
+import net.minecraft.core.HolderSet;
+import net.minecraft.core.component.DataComponentMap;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.entity.EquipmentSlotGroup;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.enchantment.Enchantment;
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
+import org.bukkit.inventory.ItemType;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.Range;
+
+import static io.papermc.paper.registry.data.util.Checks.asArgument;
+import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
+import static io.papermc.paper.registry.data.util.Checks.asConfigured;
+
+@DefaultQualifier(NonNull.class)
+public class PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry {
+
+ // Top level
+ protected @MonotonicNonNull Component description;
+
+ // Definition
+ protected @MonotonicNonNull HolderSet<Item> supportedItems;
+ protected @Nullable HolderSet<Item> primaryItems;
+ protected OptionalInt weight = OptionalInt.empty();
+ protected OptionalInt maxLevel = OptionalInt.empty();
+ protected Enchantment.@MonotonicNonNull Cost minimumCost;
+ protected Enchantment.@MonotonicNonNull Cost maximumCost;
+ protected OptionalInt anvilCost = OptionalInt.empty();
+ protected @MonotonicNonNull List<EquipmentSlotGroup> activeSlots;
+
+ // Exclusive
+ protected HolderSet<Enchantment> exclusiveWith = HolderSet.empty(); // Paper added default to empty.
+
+ // Effects
+ protected DataComponentMap effects;
+
+ protected final Conversions conversions;
+
+ public PaperEnchantmentRegistryEntry(
+ final Conversions conversions,
+ final TypedKey<org.bukkit.enchantments.Enchantment> ignoredKey,
+ final @Nullable Enchantment internal
+ ) {
+ this.conversions = conversions;
+ if (internal == null) {
+ this.effects = DataComponentMap.EMPTY;
+ return;
+ }
+
+ // top level
+ this.description = internal.description();
+
+ // definition
+ final Enchantment.EnchantmentDefinition definition = internal.definition();
+ this.supportedItems = definition.supportedItems();
+ this.primaryItems = definition.primaryItems().orElse(null);
+ this.weight = OptionalInt.of(definition.weight());
+ this.maxLevel = OptionalInt.of(definition.maxLevel());
+ this.minimumCost = definition.minCost();
+ this.maximumCost = definition.maxCost();
+ this.anvilCost = OptionalInt.of(definition.anvilCost());
+ this.activeSlots = definition.slots();
+
+ // exclusive
+ this.exclusiveWith = internal.exclusiveSet();
+
+ // effects
+ this.effects = internal.effects();
+ }
+
+ @Override
+ public net.kyori.adventure.text.Component description() {
+ return this.conversions.asAdventure(asConfigured(this.description, "description"));
+ }
+
+ @Override
+ public RegistryKeySet<ItemType> supportedItems() {
+ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, asConfigured(this.supportedItems, "supportedItems"));
+ }
+
+ @Override
+ public @Nullable RegistryKeySet<ItemType> primaryItems() {
+ return this.primaryItems == null ? null : PaperRegistrySets.convertToApi(RegistryKey.ITEM, this.primaryItems);
+ }
+
+ @Override
+ public @Range(from = 1, to = 1024) int weight() {
+ return asConfigured(this.weight, "weight");
+ }
+
+ @Override
+ public @Range(from = 1, to = 255) int maxLevel() {
+ return asConfigured(this.maxLevel, "maxLevel");
+ }
+
+ @Override
+ public EnchantmentCost minimumCost() {
+ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.minimumCost, "minimumCost");
+ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst());
+ }
+
+ @Override
+ public EnchantmentCost maximumCost() {
+ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.maximumCost, "maximumCost");
+ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst());
+ }
+
+ @Override
+ public @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost() {
+ return asConfigured(this.anvilCost, "anvilCost");
+ }
+
+ @Override
+ public List<org.bukkit.inventory.EquipmentSlotGroup> activeSlots() {
+ return Collections.unmodifiableList(Lists.transform(asConfigured(this.activeSlots, "activeSlots"), CraftEquipmentSlot::getSlot));
+ }
+
+ @Override
+ public RegistryKeySet<org.bukkit.enchantments.Enchantment> exclusiveWith() {
+ return PaperRegistrySets.convertToApi(RegistryKey.ENCHANTMENT, this.exclusiveWith);
+ }
+
+ public static final class PaperBuilder extends PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry.Builder,
+ PaperRegistryBuilder<Enchantment, org.bukkit.enchantments.Enchantment> {
+
+ public PaperBuilder(final Conversions conversions, final TypedKey<org.bukkit.enchantments.Enchantment> key, final @Nullable Enchantment internal) {
+ super(conversions, key, internal);
+ }
+
+ @Override
+ public Builder description(final net.kyori.adventure.text.Component description) {
+ this.description = this.conversions.asVanilla(asArgument(description, "description"));
+ return this;
+ }
+
+ @Override
+ public Builder supportedItems(final RegistryKeySet<ItemType> supportedItems) {
+ this.supportedItems = PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), asArgument(supportedItems, "supportedItems"));
+ return this;
+ }
+
+ @Override
+ public Builder primaryItems(final @Nullable RegistryKeySet<ItemType> primaryItems) {
+ this.primaryItems = primaryItems == null ? null : PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), primaryItems);
+ return this;
+ }
+
+ @Override
+ public Builder weight(final @Range(from = 1, to = 1024) int weight) {
+ this.weight = OptionalInt.of(Checks.asArgumentRange(weight, "weight", 1, 1024));
+ return this;
+ }
+
+ @Override
+ public Builder maxLevel(final @Range(from = 1, to = 255) int maxLevel) {
+ this.maxLevel = OptionalInt.of(Checks.asArgumentRange(maxLevel, "maxLevel", 1, 255));
+ return this;
+ }
+
+ @Override
+ public Builder minimumCost(final EnchantmentCost minimumCost) {
+ final EnchantmentCost validCost = asArgument(minimumCost, "minimumCost");
+ this.minimumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost());
+ return this;
+ }
+
+ @Override
+ public Builder maximumCost(final EnchantmentCost maximumCost) {
+ final EnchantmentCost validCost = asArgument(maximumCost, "maximumCost");
+ this.maximumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost());
+ return this;
+ }
+
+ @Override
+ public Builder anvilCost(final @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost) {
+ Preconditions.checkArgument(anvilCost >= 0, "anvilCost must be non-negative");
+ this.anvilCost = OptionalInt.of(asArgumentMin(anvilCost, "anvilCost", 0));
+ return this;
+ }
+
+ @Override
+ public Builder activeSlots(final Iterable<org.bukkit.inventory.EquipmentSlotGroup> activeSlots) {
+ this.activeSlots = Lists.newArrayList(Iterables.transform(asArgument(activeSlots, "activeSlots"), CraftEquipmentSlot::getNMSGroup));
+ return this;
+ }
+
+ @Override
+ public Builder exclusiveWith(final RegistryKeySet<org.bukkit.enchantments.Enchantment> exclusiveWith) {
+ this.exclusiveWith = PaperRegistrySets.convertToNms(Registries.ENCHANTMENT, this.conversions.lookup(), asArgument(exclusiveWith, "exclusiveWith"));
+ return this;
+ }
+
+ @Override
+ public Enchantment build() {
+ final Enchantment.EnchantmentDefinition def = new Enchantment.EnchantmentDefinition(
+ asConfigured(this.supportedItems, "supportedItems"),
+ Optional.ofNullable(this.primaryItems),
+ this.weight(),
+ this.maxLevel(),
+ asConfigured(this.minimumCost, "minimumCost"),
+ asConfigured(this.maximumCost, "maximumCost"),
+ this.anvilCost(),
+ Collections.unmodifiableList(asConfigured(this.activeSlots, "activeSlots"))
+ );
+ return new Enchantment(
+ asConfigured(this.description, "description"),
+ def,
+ this.exclusiveWith,
+ this.effects
+ );
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..18f9463ae23ba2d9c65ffb7531a87c925a5a8d6f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java
@@ -0,0 +1,57 @@
+package io.papermc.paper.registry.data;
+
+import io.papermc.paper.registry.PaperRegistryBuilder;
+import io.papermc.paper.registry.data.util.Conversions;
+import java.util.OptionalInt;
+import net.minecraft.world.level.gameevent.GameEvent;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.Range;
+
+import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
+import static io.papermc.paper.registry.data.util.Checks.asConfigured;
+
+@DefaultQualifier(NonNull.class)
+public class PaperGameEventRegistryEntry implements GameEventRegistryEntry {
+
+ protected OptionalInt range = OptionalInt.empty();
+
+ public PaperGameEventRegistryEntry(
+ final Conversions ignoredConversions,
+ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> ignoredKey,
+ final @Nullable GameEvent nms
+ ) {
+ if (nms == null) return;
+
+ this.range = OptionalInt.of(nms.notificationRadius());
+ }
+
+ @Override
+ public @Range(from = 0, to = Integer.MAX_VALUE) int range() {
+ return asConfigured(this.range, "range");
+ }
+
+ public static final class PaperBuilder extends PaperGameEventRegistryEntry implements GameEventRegistryEntry.Builder,
+ PaperRegistryBuilder<GameEvent, org.bukkit.GameEvent> {
+
+ public PaperBuilder(
+ final Conversions conversions,
+ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> key,
+ final @Nullable GameEvent nms
+ ) {
+ super(conversions, key, nms);
+ }
+
+ @Override
+ public GameEventRegistryEntry.Builder range(final @Range(from = 0, to = Integer.MAX_VALUE) int range) {
+ this.range = OptionalInt.of(asArgumentMin(range, "range", 0));
+ return this;
+ }
+
+ @Override
+ public GameEvent build() {
+ return new GameEvent(this.range());
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/data/util/Checks.java b/src/main/java/io/papermc/paper/registry/data/util/Checks.java
new file mode 100644
index 0000000000000000000000000000000000000000..3241a94731fe8163876614efdcf30f8b551535af
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/util/Checks.java
@@ -0,0 +1,48 @@
+package io.papermc.paper.registry.data.util;
+
+import java.util.OptionalInt;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class Checks {
+
+ public static <T> T asConfigured(final @Nullable T value, final String field) {
+ if (value == null) {
+ throw new IllegalStateException(field + " has not been configured");
+ }
+ return value;
+ }
+
+ public static int asConfigured(final OptionalInt value, final String field) {
+ if (value.isEmpty()) {
+ throw new IllegalStateException(field + " has not been configured");
+ }
+ return value.getAsInt();
+ }
+
+ public static <T> T asArgument(final @Nullable T value, final String field) {
+ if (value == null) {
+ throw new IllegalArgumentException("argument " + field + " cannot be null");
+ }
+ return value;
+ }
+
+ public static int asArgumentRange(final int value, final String field, final int min, final int max) {
+ if (value < min || value > max) {
+ throw new IllegalArgumentException("argument " + field + " must be [" + min + ", " + max + "]");
+ }
+ return value;
+ }
+
+ public static int asArgumentMin(final int value, final String field, final int min) {
+ if (value < min) {
+ throw new IllegalArgumentException("argument " + field + " must be [" + min + ",+inf)");
+ }
+ return value;
+ }
+
+ private Checks() {
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
index cc3c56224e64816b885c0131ce2a800a2efe3113..7acd9c71c5c4b487e792b8c36a8e52e10b691e98 100644
--- a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
+++ b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
@@ -85,7 +85,7 @@ public record GameEvent(int notificationRadius) {
}
private static Holder.Reference<GameEvent> register(String id, int range) {
- return Registry.registerForHolder(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range));
+ return io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerForHolderWithListeners(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range)); // Paper - run with listeners
}
public static record Context(@Nullable Entity sourceEntity, @Nullable BlockState affectedState) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
index ac9b4328cd55a68664a3f71186bc9a7be7cd9658..ea9fe1f8b1a1685ea975eba0ca418a831006065a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
@@ -18,10 +18,12 @@ public class CraftGameEvent extends GameEvent implements Handleable<net.minecraf
}
private final NamespacedKey key;
+ private final net.minecraft.resources.ResourceKey<net.minecraft.world.level.gameevent.GameEvent> handleKey; // Paper
private final net.minecraft.world.level.gameevent.GameEvent handle;
public CraftGameEvent(NamespacedKey key, net.minecraft.world.level.gameevent.GameEvent handle) {
this.key = key;
+ this.handleKey = net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.GAME_EVENT, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(key)); // Paper
this.handle = handle;
}
@@ -30,6 +32,18 @@ public class CraftGameEvent extends GameEvent implements Handleable<net.minecraf
return this.handle;
}
+ // Paper start
+ @Override
+ public int getRange() {
+ return this.handle.notificationRadius();
+ }
+
+ @Override
+ public int getVibrationLevel() {
+ return net.minecraft.world.level.gameevent.vibrations.VibrationSystem.getGameEventFrequency(this.handleKey);
+ }
+ // Paper end
+
@NotNull
@Override
public NamespacedKey getKey() {
diff --git a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
index 47b8ebac8496179008b8932c5ca2aadc274e24e0..814675bf67fd02e8cd2311dce60eeef651ef16f1 100644
--- a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
+++ b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
@@ -1,11 +1,16 @@
package io.papermc.paper.registry;
+import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
+import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
import io.papermc.paper.registry.data.util.Conversions;
import java.util.List;
import java.util.Map;
import net.minecraft.core.Registry;
+import net.minecraft.core.registries.Registries;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
+import net.minecraft.world.item.enchantment.Enchantment;
+import net.minecraft.world.level.gameevent.GameEvent;
import org.bukkit.support.RegistryHelper;
import org.bukkit.support.environment.AllFeatures;
import org.junit.jupiter.api.Disabled;
@@ -14,16 +19,18 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
@AllFeatures
class RegistryBuilderTest {
static List<Arguments> registries() {
return List.of(
+ arguments(Registries.ENCHANTMENT, (PaperRegistryBuilder.Filler<Enchantment, org.bukkit.enchantments.Enchantment, PaperEnchantmentRegistryEntry.PaperBuilder>) PaperEnchantmentRegistryEntry.PaperBuilder::new),
+ arguments(Registries.GAME_EVENT, (PaperRegistryBuilder.Filler<GameEvent, org.bukkit.GameEvent, PaperGameEventRegistryEntry.PaperBuilder>) PaperGameEventRegistryEntry.PaperBuilder::new)
);
}
- @Disabled
@ParameterizedTest
@MethodSource("registries")
<M, T> void testEquality(final ResourceKey<? extends Registry<M>> resourceKey, final PaperRegistryBuilder.Filler<M, T, ?> filler) {

View file

@ -1,302 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Tue, 14 May 2024 11:57:43 -0700
Subject: [PATCH] Proxy ItemStack to CraftItemStack
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index efb7fb8dbaa7446e394f55b021692c11a25fd29f..a3c6d2cbdce60b1cf935d798568b8bb5d97e1229 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -26,15 +26,57 @@ import org.jetbrains.annotations.ApiStatus;
@DelegateDeserialization(ItemStack.class)
public final class CraftItemStack extends ItemStack {
- // Paper start - MC Utils
- public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
- if (bukkit instanceof CraftItemStack craftItemStack) {
- return craftItemStack.handle != null ? craftItemStack.handle : net.minecraft.world.item.ItemStack.EMPTY;
+ // Paper start - delegate api-ItemStack to CraftItemStack
+ private static final java.lang.invoke.VarHandle API_ITEM_STACK_CRAFT_DELEGATE_FIELD;
+ static {
+ try {
+ API_ITEM_STACK_CRAFT_DELEGATE_FIELD = java.lang.invoke.MethodHandles.privateLookupIn(
+ ItemStack.class,
+ java.lang.invoke.MethodHandles.lookup()
+ ).findVarHandle(ItemStack.class, "craftDelegate", ItemStack.class);
+ } catch (final IllegalAccessException | NoSuchFieldException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+
+ private static CraftItemStack getCraftStack(final ItemStack bukkit) {
+ if (bukkit instanceof final CraftItemStack craftItemStack) {
+ return craftItemStack;
} else {
- return asNMSCopy(bukkit);
+ return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
}
}
+ @Override
+ public int hashCode() {
+ if (this.handle == null || this.handle.isEmpty()) {
+ return net.minecraft.world.item.ItemStack.EMPTY.hashCode();
+ } else {
+ int hash = net.minecraft.world.item.ItemStack.hashItemAndComponents(this.handle);
+ hash = hash * 31 + this.handle.getCount();
+ return hash;
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false;
+ final CraftItemStack craftStack = getCraftStack(bukkit);
+ if (this.handle == craftStack.handle) return true;
+ else if (this.handle == null || craftStack.handle == null) return false;
+ else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
+ else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
+ }
+ // Paper end
+
+ // Paper start - MC Utils
+ public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
+ final CraftItemStack craftItemStack = getCraftStack(bukkit);
+ return craftItemStack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : craftItemStack.handle;
+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
+ }
+
public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack old, ItemStack newInstance) {
return old == newInstance ? unwrap(old) : asNMSCopy(newInstance);
}
@@ -48,25 +90,13 @@ public final class CraftItemStack extends ItemStack {
// Paper end - override isEmpty to use vanilla's impl
public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) {
- if (original instanceof CraftItemStack) {
- CraftItemStack stack = (CraftItemStack) original;
- return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
- }
- if (original == null || original.isEmpty()) { // Paper - override isEmpty to use vanilla's impl; use isEmpty
+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
+ if (original == null || original.isEmpty()) {
return net.minecraft.world.item.ItemStack.EMPTY;
}
-
- Item item = CraftItemType.bukkitToMinecraft(original.getType());
-
- if (item == null) {
- return net.minecraft.world.item.ItemStack.EMPTY;
- }
-
- net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(item, original.getAmount());
- if (original.hasItemMeta()) {
- CraftItemStack.setItemMeta(stack, original.getItemMeta());
- }
- return stack;
+ final CraftItemStack stack = getCraftStack(original);
+ return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
}
// Paper start
@@ -89,14 +119,10 @@ public final class CraftItemStack extends ItemStack {
* Copies the NMS stack to return as a strictly-Bukkit stack
*/
public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStack original) {
- if (original.isEmpty()) {
- return new ItemStack(Material.AIR);
- }
- ItemStack stack = new ItemStack(CraftItemType.minecraftToBukkit(original.getItem()), original.getCount());
- if (CraftItemStack.hasItemMeta(original)) {
- stack.setItemMeta(CraftItemStack.getItemMeta(original));
- }
- return stack;
+ // Paper start - no such thing as a "strictly-Bukkit stack" anymore
+ // we copy the stack since it should be a complete copy not a mirror
+ return asCraftMirror(original.copy());
+ // Paper end
}
public static CraftItemStack asCraftMirror(net.minecraft.world.item.ItemStack original) {
@@ -317,11 +343,7 @@ public final class CraftItemStack extends ItemStack {
@Override
public CraftItemStack clone() {
- CraftItemStack itemStack = (CraftItemStack) super.clone();
- if (this.handle != null) {
- itemStack.handle = this.handle.copy();
- }
- return itemStack;
+ return new org.bukkit.craftbukkit.inventory.CraftItemStack(this.handle != null ? this.handle.copy() : null); // Paper
}
@Override
@@ -424,22 +446,14 @@ public final class CraftItemStack extends ItemStack {
if (stack == this) {
return true;
}
- if (!(stack instanceof CraftItemStack)) {
- return stack.getClass() == ItemStack.class && stack.isSimilar(this);
- }
-
- CraftItemStack that = (CraftItemStack) stack;
+ final CraftItemStack that = getCraftStack(stack); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
if (this.handle == that.handle) {
return true;
}
if (this.handle == null || that.handle == null) {
return false;
}
- Material comparisonType = CraftLegacy.fromLegacy(that.getType()); // This may be called from legacy item stacks, try to get the right material
- if (!(comparisonType == this.getType() && this.getDurability() == that.getDurability())) {
- return false;
- }
- return this.hasItemMeta() ? that.hasItemMeta() && this.handle.getComponents().equals(that.handle.getComponents()) : !that.hasItemMeta();
+ return net.minecraft.world.item.ItemStack.isSameItemSameComponents(this.handle, that.handle); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
index d03f4a767f6c7fe7d6bcef20e6676c39d9657584..bae3dd5fc67e6b3d98a5e63ffbf639c5042f8843 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
@@ -100,13 +100,14 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
@NotNull
@Override
public ItemStack createItemStack(final int amount, @Nullable final Consumer<? super M> metaConfigurator) {
- final ItemStack itemStack = new ItemStack(this.asMaterial(), amount);
+ // Paper start - re-implement to return CraftItemStack
+ final net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(this.item, amount);
+ final CraftItemStack mirror = CraftItemStack.asCraftMirror(stack);
if (metaConfigurator != null) {
- final ItemMeta itemMeta = itemStack.getItemMeta();
- metaConfigurator.accept((M) itemMeta);
- itemStack.setItemMeta(itemMeta);
+ mirror.editMeta(this.getItemMetaClass(), metaConfigurator);
}
- return itemStack;
+ return mirror;
+ // Paper start - reimplement to return CraftItemStack
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
index 6cc9d7a9e6d4bfdc27e52fc581b2bb832616f121..6930d0afb230a88aa813b02e4d55c95d3a049688 100644
--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
@@ -678,4 +678,16 @@ public class MaterialRerouting {
return itemStack.withType(material);
}
// Paper end - register paper API specific material consumers in rerouting
+
+ // Paper start - methods added post 1.13, no-op
+ @RerouteStatic("org/bukkit/inventory/ItemStack")
+ public static ItemStack of(final Material material) {
+ return ItemStack.of(material);
+ }
+
+ @RerouteStatic("org/bukkit/inventory/ItemStack")
+ public static ItemStack of(final Material material, final int amount) {
+ return ItemStack.of(material, amount);
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 68a6cd43042e87501f5bd48565222638dd58a1cf..6adc18c40d5d62e2ebc8deec197cec630a366937 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -689,6 +689,13 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
// Paper end - hack to get tags for non server-backed registries
+ // Paper start - proxy ItemStack
+ @Override
+ public org.bukkit.inventory.ItemStack createEmptyStack() {
+ return CraftItemStack.asCraftMirror(null);
+ }
+ // Paper end - proxy ItemStack
+
/**
* This helper class represents the different NBT Tags.
* <p>
diff --git a/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed45bfa577579afcbd54d655c3b5d05d6c6f3e86
--- /dev/null
+++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
@@ -0,0 +1,53 @@
+package io.papermc.paper.configuration;
+
+import org.bukkit.Material;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.support.environment.VanillaFeature;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public abstract class ConfigurationSectionTest {
+ public abstract ConfigurationSection getConfigurationSection();
+
+ @Test
+ public void testGetItemStack_String() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+
+ section.set(key, value);
+
+ assertEquals(value, section.getItemStack(key));
+ assertNull(section.getString("doesntExist"));
+ }
+
+ @Test
+ public void testGetItemStack_String_ItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+ ItemStack def = new ItemStack(Material.STONE, 1);
+
+ section.set(key, value);
+
+ assertEquals(value, section.getItemStack(key, def));
+ assertEquals(def, section.getItemStack("doesntExist", def));
+ }
+
+ @Test
+ public void testIsItemStack() {
+ ConfigurationSection section = getConfigurationSection();
+ String key = "exists";
+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
+
+ section.set(key, value);
+
+ assertTrue(section.isItemStack(key));
+ assertFalse(section.isItemStack("doesntExist"));
+ }
+}
diff --git a/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c00085328ce8a00fc274632a556ab27660fa57ed
--- /dev/null
+++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
@@ -0,0 +1,13 @@
+package io.papermc.paper.configuration;
+
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.MemoryConfiguration;
+import org.bukkit.support.environment.Normal;
+
+@Normal
+public class MemorySectionTest extends ConfigurationSectionTest {
+ @Override
+ public ConfigurationSection getConfigurationSection() {
+ return new MemoryConfiguration().createSection("section");
+ }
+}

View file

@ -1,271 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 12 Jun 2024 10:29:40 -0700
Subject: [PATCH] Make a PDC view accessible directly from ItemStack
diff --git a/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..122c32e82b299cafd7d0c6a9f4818523470c9f05
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
@@ -0,0 +1,120 @@
+package io.papermc.paper.persistence;
+
+import com.google.common.base.Preconditions;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.Tag;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataAdapterContext;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
+import org.bukkit.persistence.PersistentDataAdapterContext;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public abstract class PaperPersistentDataContainerView implements PersistentDataContainerView {
+
+ protected final CraftPersistentDataTypeRegistry registry;
+ protected final CraftPersistentDataAdapterContext adapterContext;
+
+ public PaperPersistentDataContainerView(final CraftPersistentDataTypeRegistry registry) {
+ this.registry = registry;
+ this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
+ }
+
+ public abstract @Nullable Tag getTag(final String key);
+
+ public abstract CompoundTag toTagCompound();
+
+ @Override
+ public <P, C> boolean has(final NamespacedKey key, final PersistentDataType<P, C> type) {
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return false;
+ }
+
+ return this.registry.isInstanceOf(type, value);
+ }
+
+ @Override
+ public boolean has(final NamespacedKey key) {
+ Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
+ return this.getTag(key.toString()) != null;
+ }
+
+ @Override
+ public <P, C> @Nullable C get(final NamespacedKey key, final PersistentDataType<P, C> type) {
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return null;
+ }
+
+ return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
+ }
+
+ @Override
+ public <P, C> C getOrDefault(final NamespacedKey key, final PersistentDataType<P, C> type, final C defaultValue) {
+ final C c = this.get(key, type);
+ return c != null ? c : defaultValue;
+ }
+
+ @Override
+ public Set<NamespacedKey> getKeys() {
+ final Set<String> names = this.toTagCompound().getAllKeys();
+ final Set<NamespacedKey> keys = new HashSet<>(names.size());
+ names.forEach(key -> {
+ final String[] keyPart = key.split(":", 2);
+ if (keyPart.length == 2) {
+ keys.add(new NamespacedKey(keyPart[0], keyPart[1]));
+ }
+ });
+ return Collections.unmodifiableSet(keys);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.toTagCompound().isEmpty();
+ }
+
+ @Override
+ public void copyTo(final PersistentDataContainer other, final boolean replace) {
+ Preconditions.checkArgument(other != null, "The target container cannot be null");
+ final CraftPersistentDataContainer target = (CraftPersistentDataContainer) other;
+ final CompoundTag tag = this.toTagCompound();
+ for (final String key : tag.getAllKeys()) {
+ if (replace || !target.getRaw().containsKey(key)) {
+ target.getRaw().put(key, tag.get(key).copy());
+ }
+ }
+ }
+
+ @Override
+ public PersistentDataAdapterContext getAdapterContext() {
+ return this.adapterContext;
+ }
+
+ @Override
+ public byte[] serializeToBytes() throws IOException {
+ final CompoundTag root = this.toTagCompound();
+ final ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+ try (final DataOutputStream dataOutput = new DataOutputStream(byteArrayOutput)) {
+ NbtIo.write(root, dataOutput);
+ return byteArrayOutput.toByteArray();
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index a3c6d2cbdce60b1cf935d798568b8bb5d97e1229..6081c588c61406d0d21a15e8e6140d5d5240f0a8 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -484,4 +484,34 @@ public final class CraftItemStack extends ItemStack {
return mirrored;
}
// Paper end
+
+ // Paper start - pdc
+ private net.minecraft.nbt.CompoundTag getPdcTag() {
+ if (this.handle == null) {
+ return new net.minecraft.nbt.CompoundTag();
+ }
+ final net.minecraft.world.item.component.CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY);
+ // getUnsafe is OK here because we are only ever *reading* the data so immutability is preserved
+ //noinspection deprecation
+ return customData.getUnsafe().getCompound("PublicBukkitValues");
+ }
+
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ private final io.papermc.paper.persistence.PaperPersistentDataContainerView pdcView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(REGISTRY) {
+
+ @Override
+ public net.minecraft.nbt.CompoundTag toTagCompound() {
+ return CraftItemStack.this.getPdcTag();
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag getTag(final String key) {
+ return CraftItemStack.this.getPdcTag().get(key);
+ }
+ };
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
+ return this.pdcView;
+ }
+ // Paper end - pdc
}
diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
index f55fdd57ced259ad5a95878840e98ffaa3db2e05..9d867f1659433ea15f281c8b441db7e339013100 100644
--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
@@ -16,11 +16,10 @@ import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
-public class CraftPersistentDataContainer implements PersistentDataContainer {
+public class CraftPersistentDataContainer extends io.papermc.paper.persistence.PaperPersistentDataContainerView implements PersistentDataContainer { // Paper - split up view and mutable
private final Map<String, Tag> customDataTags = new HashMap<>();
- private final CraftPersistentDataTypeRegistry registry;
- private final CraftPersistentDataAdapterContext adapterContext;
+ // Paper - move to PersistentDataContainerView
public CraftPersistentDataContainer(Map<String, Tag> customTags, CraftPersistentDataTypeRegistry registry) {
this(registry);
@@ -28,10 +27,15 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
}
public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) {
- this.registry = registry;
- this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
+ super(registry); // Paper - move to PersistentDataContainerView
}
+ // Paper start
+ @Override
+ public Tag getTag(final String key) {
+ return this.customDataTags.get(key);
+ }
+ // Paper end
@Override
public <T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value) {
@@ -42,44 +46,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
this.customDataTags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, this.adapterContext)));
}
- @Override
- public <T, Z> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
- Preconditions.checkArgument(type != null, "The provided type cannot be null");
-
- Tag value = this.customDataTags.get(key.toString());
- if (value == null) {
- return false;
- }
-
- return this.registry.isInstanceOf(type, value);
- }
-
- @Override
- public boolean has(NamespacedKey key) {
- Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
- return this.customDataTags.get(key.toString()) != null;
- }
-
- @Override
- public <T, Z> Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
- Preconditions.checkArgument(type != null, "The provided type cannot be null");
-
- Tag value = this.customDataTags.get(key.toString());
- if (value == null) {
- return null;
- }
-
- return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
- }
-
- @NotNull
- @Override
- public <T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue) {
- Z z = this.get(key, type);
- return z != null ? z : defaultValue;
- }
+ // Paper - move to PersistentDataContainerView
@NotNull
@Override
@@ -186,16 +153,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
// Paper end
// Paper start - byte array serialization
- @Override
- public byte[] serializeToBytes() throws java.io.IOException {
- final net.minecraft.nbt.CompoundTag root = this.toTagCompound();
- final java.io.ByteArrayOutputStream byteArrayOutput = new java.io.ByteArrayOutputStream();
- try (final java.io.DataOutputStream dataOutput = new java.io.DataOutputStream(byteArrayOutput)) {
- net.minecraft.nbt.NbtIo.write(root, dataOutput);
- return byteArrayOutput.toByteArray();
- }
- }
-
+ // Paper - move to PersistentDataContainerView
@Override
public void readFromBytes(final byte[] bytes, final boolean clear) throws java.io.IOException {
if (clear) {

View file

@ -1,135 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Mon, 1 Jul 2024 11:58:49 -0700
Subject: [PATCH] Prioritize Minecraft commands in function parsing and command
blocks
diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
index a4d5d7017e0be79844b996de85a63cad5f8488bc..770aec1976bd391eb5712b57b6c6a0290b4723a8 100644
--- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java
+++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
@@ -298,7 +298,7 @@ public class CommandDispatcher<S> {
List<ParseResults<S>> potentials = null;
final int cursor = originalReader.getCursor();
- for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
+ for (final CommandNode<S> child : node.getRelevantNodes(originalReader, source)) { // Paper - prioritize mc commands in function parsing
if (!child.canUse(source)) {
continue;
}
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
index 03ce8a2abb6dceaa922dcce7f3adbc228bbde4bc..dc76fcf4c6cc6cd65ce117b1855c15ede60f30ab 100644
--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
@@ -173,6 +173,12 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
protected abstract String getSortedKey();
public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
+ // Paper start - prioritize mc commands in function parsing
+ return this.getRelevantNodes(input, null);
+ }
+ @org.jetbrains.annotations.ApiStatus.Internal
+ public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input, final Object source) {
+ // Paper end - prioritize mc commands in function parsing
if (this.literals.size() > 0) {
final int cursor = input.getCursor();
while (input.canRead() && input.peek() != ' ') {
@@ -180,7 +186,21 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
}
final String text = input.getString().substring(cursor, input.getCursor());
input.setCursor(cursor);
- final LiteralCommandNode<S> literal = this.literals.get(text);
+ // Paper start - prioritize mc commands in function parsing
+ LiteralCommandNode<S> literal = null;
+ if (source instanceof CommandSourceStack css && css.source == net.minecraft.commands.CommandSource.NULL) {
+ if (!text.contains(":")) {
+ literal = this.literals.get("minecraft:" + text);
+ }
+ } else if (source instanceof CommandSourceStack css && css.source instanceof net.minecraft.world.level.BaseCommandBlock) {
+ if (css.getServer().server.getCommandBlockOverride(text) && !text.contains(":")) {
+ literal = this.literals.get("minecraft:" + text);
+ }
+ }
+ if (literal == null) {
+ literal = this.literals.get(text);
+ }
+ // Paper end - prioritize mc commands in function parsing
if (literal != null) {
return Collections.singleton(literal);
} else {
diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
index 85a890403645f0f9d381e85b48efcae126673945..bcc27fec043a57eb5064934c967982deff9cdee4 100644
--- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
+++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
@@ -23,11 +23,19 @@ import java.util.function.Predicate;
public class LiteralCommandNode<S> extends CommandNode<S> {
private final String literal;
private final String literalLowerCase;
+ private final String nonPrefixed; // Paper - prioritize mc commands in function parsing
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
super(command, requirement, redirect, modifier, forks);
this.literal = literal;
this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
+ // Paper start - prioritize mc commands in function parsing
+ if (literal.startsWith("minecraft:")) {
+ this.nonPrefixed = literal.substring("minecraft:".length());
+ } else {
+ this.nonPrefixed = null;
+ }
+ // Paper end - prioritize mc commands in function parsing
}
public String getLiteral() {
@@ -42,7 +50,12 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
@Override
public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
final int start = reader.getCursor();
- final int end = parse(reader);
+ // Paper start - prioritize mc commands in function parsing
+ int end = parse(reader, false);
+ if (end == -1 && this.nonPrefixed != null) {
+ end = parse(reader, true);
+ }
+ // Paper end - prioritize mc commands in function parsing
if (end > -1) {
contextBuilder.withNode(this, StringRange.between(start, end));
return;
@@ -51,7 +64,10 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect().createWithContext(reader, literal);
}
- private int parse(final StringReader reader) {
+ // Paper start - prioritize mc commands in function parsing
+ private int parse(final StringReader reader, final boolean secondPass) {
+ String literal = secondPass ? this.nonPrefixed : this.literal;
+ // Paper end - prioritize mc commands in function parsing
final int start = reader.getCursor();
if (reader.canRead(literal.length())) {
final int end = start + literal.length();
@@ -78,7 +94,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
@Override
public boolean isValidInput(final String input) {
- return parse(new StringReader(input)) > -1;
+ return parse(new StringReader(input), false) > -1; // Paper - prioritize mc commands in function parsing
}
@Override
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 3e454515360c22a26c9329e4032d525579110d7e..1d1e76de60e40224f5cb81893f9ee50fe987badb 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -310,10 +310,7 @@ public class Commands {
// Paper - Fix permission levels for command blocks
- // Handle vanilla commands;
- if (sender.getLevel().getCraftServer().getCommandBlockOverride(args[0])) {
- args[0] = "minecraft:" + args[0];
- }
+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher
String newCommand = joiner.join(args);
this.performPrefixedCommand(sender, newCommand, newCommand);

View file

@ -1,78 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: lukas81298 <lukas81298@gommehd.net>
Date: Fri, 22 Jan 2021 21:50:18 +0100
Subject: [PATCH] optimize dirt and snow spreading
diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
index 5a39e8d359dc13383711e49ffb2d1294dad26192..b7165ec19bef1a07f6618fc0429d86cda1b08da4 100644
--- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
@@ -18,8 +18,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
}
private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ return canBeGrass(world.getChunk(pos), state, world, pos);
+ }
+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
+ // Paper end - Perf: optimize dirt and snow spreading
BlockPos blockposition1 = pos.above();
- BlockState iblockdata1 = world.getBlockState(blockposition1);
+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading
if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) {
return true;
@@ -36,15 +41,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ return canPropagate(world.getChunk(pos), state, world, pos);
+ }
+
+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
+ // Paper end - Perf: optimize dirt and snow spreading
BlockPos blockposition1 = pos.above();
- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER);
+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
}
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
+ if (cachedBlockChunk == null) { // Is this needed?
+ return;
+ }
+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
+ // Paper end - Perf: optimize dirt and snow spreading
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
return;
@@ -57,9 +74,19 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
for (int i = 0; i < 4; ++i) {
BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
-
- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) {
- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, world.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
+ // Paper start - Perf: optimize dirt and snow spreading
+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) {
+ continue;
+ }
+ net.minecraft.world.level.chunk.ChunkAccess access;
+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) {
+ access = cachedBlockChunk;
+ } else {
+ access = world.getChunkAt(blockposition1);
+ }
+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) {
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, access.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
+ // Paper end - Perf: optimize dirt and snow spreading
}
}
}

View file

@ -1,20 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 17 Jun 2024 17:41:09 -0700
Subject: [PATCH] Fix NPE for Jukebox#setRecord
Fallback to the global registry if no level exists
diff --git a/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
index 1497e76b548ad76b5aaa297bdd35723e6a8f1f8d..c5069730b25e6f0dfb4e5db3271c91116b485f58 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
@@ -200,7 +200,7 @@ public class JukeboxBlockEntity extends BlockEntity implements Clearable, Contai
public void setSongItemWithoutPlaying(ItemStack itemstack, long ticksSinceSongStarted) { // CraftBukkit - add argument
this.item = itemstack;
this.jukeboxSongPlayer.song = null; // CraftBukkit - reset
- JukeboxSong.fromStack(this.level.registryAccess(), itemstack).ifPresent((holder) -> {
+ JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), itemstack).ifPresent((holder) -> { // Paper - fallback to other RegistyrAccess if no level
this.jukeboxSongPlayer.setSongWithoutPlaying(holder, ticksSinceSongStarted); // CraftBukkit - add argument
});
// CraftBukkit start - add null check for level