mirror of
https://github.com/PaperMC/Paper.git
synced 2025-11-18 20:54:27 +01:00
Merge remote-tracking branch 'public/main' into dev/snapshot
This commit is contained in:
commit
05145d0981
@ -476,6 +476,17 @@ If you use Maven to build your plugin:
|
||||
If you use Windows and don't usually build using WSL, you might not need to
|
||||
do this.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### IntelliJ IDEA
|
||||
|
||||
- Under `Settings > Appearance & Behavior > System Settings`, disable
|
||||
`Sync external changes: Periodically when the IDE is inactive (experimental)`.
|
||||
When disabled, the IDE will not attempt to reindex files while patches are applying
|
||||
unless you interact with the IDE during the process. This avoids severe slowdowns and freezes.
|
||||
- Under `Settings > Appearance & Behavior > System Settings`, you may also want to
|
||||
disable `Reopen projects on startup` to avoid freeze loops.
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### My commit doesn't need a build, what do I do?
|
||||
|
||||
@ -422,7 +422,7 @@ public class ParticleBuilder implements Cloneable {
|
||||
|
||||
/**
|
||||
* Sets the particle Color.
|
||||
* Only valid for particles with a data type of {@link Color} or {@link Particle.DustOptions}.
|
||||
* Only valid for particles with a data type of {@link Color}, {@link Particle.DustOptions} or {@link Particle.Spell}.
|
||||
*
|
||||
* @param color the new particle color
|
||||
* @return a reference to this object.
|
||||
@ -435,33 +435,37 @@ public class ParticleBuilder implements Cloneable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the particle Color and size.
|
||||
* Only valid for particles with a data type of {@link Particle.DustOptions}.
|
||||
* Sets the particle Color and size or power.
|
||||
* Only valid for particles with a data type of {@link Particle.DustOptions} or {@link Particle.Spell}.
|
||||
*
|
||||
* @param color the new particle color
|
||||
* @param size the size of the particle
|
||||
* @param value the size or power of the particle
|
||||
* @return a reference to this object.
|
||||
*/
|
||||
public ParticleBuilder color(final @Nullable Color color, final float size) {
|
||||
if (this.particle.getDataType() != Particle.DustOptions.class && color != null) {
|
||||
throw new IllegalStateException("The combination of Color and size cannot be set on this particle type.");
|
||||
public ParticleBuilder color(final @Nullable Color color, final float value) {
|
||||
if (this.particle.getDataType() != Particle.DustOptions.class && this.particle.getDataType() != Particle.Spell.class && color != null) {
|
||||
throw new IllegalStateException("The combination of Color and float value cannot be set on this particle type.");
|
||||
}
|
||||
|
||||
// We don't officially support reusing these objects, but here we go
|
||||
if (color == null) {
|
||||
if (this.data instanceof Particle.DustOptions) {
|
||||
if (this.data instanceof Particle.DustOptions || this.data instanceof Particle.Spell) {
|
||||
return this.data(null);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return this.data(new Particle.DustOptions(color, size));
|
||||
if (this.particle.getDataType() == Particle.DustOptions.class) {
|
||||
return this.data(new Particle.DustOptions(color, value));
|
||||
} else {
|
||||
return this.data(new Particle.Spell(color, value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the particle Color.
|
||||
* Only valid for particles with a data type of {@link Color} or {@link Particle.DustOptions}.
|
||||
* Only valid for particles with a data type of {@link Color}, {@link Particle.DustOptions} or {@link Particle.Spell}.
|
||||
*
|
||||
* @param r red color component
|
||||
* @param g green color component
|
||||
@ -474,7 +478,7 @@ public class ParticleBuilder implements Cloneable {
|
||||
|
||||
/**
|
||||
* Sets the particle Color.
|
||||
* Only valid for particles with a data type of {@link Color} or {@link Particle.DustOptions}.
|
||||
* Only valid for particles with a data type of {@link Color}, {@link Particle.DustOptions} or {@link Particle.Spell}.
|
||||
* <p>
|
||||
* This method detects if the provided color integer is in RGB or ARGB format.
|
||||
* If the alpha channel is zero, it treats the color as RGB. Otherwise, it treats it as ARGB.
|
||||
@ -493,7 +497,7 @@ public class ParticleBuilder implements Cloneable {
|
||||
|
||||
/**
|
||||
* Sets the particle Color.
|
||||
* Only valid for particles with a data type of {@link Color} or {@link Particle.DustOptions}.
|
||||
* Only valid for particles with a data type of {@link Color}, {@link Particle.DustOptions} or {@link Particle.Spell}.
|
||||
*
|
||||
* @param a alpha color component
|
||||
* @param r red color component
|
||||
|
||||
@ -3490,11 +3490,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
|
||||
public void updateCommands();
|
||||
|
||||
/**
|
||||
* Open a {@link Material#WRITTEN_BOOK} for a Player
|
||||
* Open an ItemStack with {@link io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT} for a Player
|
||||
*
|
||||
* @param book The book to open for this player
|
||||
* @param book the item with written book content to open for this player
|
||||
* @throws IllegalArgumentException if the ItemStack is null, empty or doesn't have a {@link io.papermc.paper.datacomponent.DataComponentTypes#WRITTEN_BOOK_CONTENT}
|
||||
*/
|
||||
public void openBook(ItemStack book);
|
||||
void openBook(ItemStack book);
|
||||
|
||||
/**
|
||||
* Open a Sign for editing by the Player.
|
||||
|
||||
@ -16,7 +16,7 @@ This lets us get faster foreach iteration, as well as avoids map lookups on
|
||||
the values when needed.
|
||||
|
||||
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d718893d0 100644
|
||||
index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..5894260481ea395c26a5b506e38530f7b2032b33 100644
|
||||
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
@@ -48,8 +48,12 @@ public class PathFinder {
|
||||
@ -52,15 +52,14 @@ index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d
|
||||
this.openSet.clear();
|
||||
this.openSet.insert(node);
|
||||
boolean asBoolean = this.captureDebug.getAsBoolean();
|
||||
- Set<Node> set1 = asBoolean ? new HashSet<>() : Set.of();
|
||||
+ // Set<Node> set1 = asBoolean ? new HashSet<>() : Set.of(); // Paper - unused debug
|
||||
Set<Node> set1 = asBoolean ? new HashSet<>() : Set.of();
|
||||
int i = 0;
|
||||
- Set<Target> set2 = Sets.newHashSetWithExpectedSize(set.size());
|
||||
+ List<Map.Entry<Target, BlockPos>> entryList = Lists.newArrayListWithExpectedSize(positions.size()); // Paper - optimize collection
|
||||
int i1 = (int)(this.maxVisitedNodes * maxVisitedNodesMultiplier);
|
||||
|
||||
while (!this.openSet.isEmpty()) {
|
||||
@@ -81,20 +85,21 @@ public class PathFinder {
|
||||
@@ -81,14 +85,18 @@ public class PathFinder {
|
||||
Node node1 = this.openSet.pop();
|
||||
node1.closed = true;
|
||||
|
||||
@ -82,13 +81,7 @@ index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d
|
||||
break;
|
||||
}
|
||||
|
||||
- if (asBoolean) {
|
||||
- set1.add(node1);
|
||||
- }
|
||||
|
||||
if (!(node1.distanceTo(node) >= maxRange)) {
|
||||
int neighbors = this.nodeEvaluator.getNeighbors(this.neighbors, node1);
|
||||
@@ -107,7 +112,7 @@ public class PathFinder {
|
||||
@@ -107,7 +115,7 @@ public class PathFinder {
|
||||
if (node2.walkedDistance < maxRange && (!node2.inOpenSet() || f1 < node2.g)) {
|
||||
node2.cameFrom = node1;
|
||||
node2.g = f1;
|
||||
@ -97,7 +90,7 @@ index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d
|
||||
if (node2.inOpenSet()) {
|
||||
this.openSet.changeCost(node2, node2.g + node2.h);
|
||||
} else {
|
||||
@@ -119,34 +124,34 @@ public class PathFinder {
|
||||
@@ -119,34 +127,41 @@ public class PathFinder {
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,13 +101,6 @@ index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d
|
||||
- : set.stream()
|
||||
- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targets.get(pathfinder), false))
|
||||
- .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
|
||||
- profilerFiller.pop();
|
||||
- if (optional.isEmpty()) {
|
||||
- return null;
|
||||
- } else {
|
||||
- Path path = optional.get();
|
||||
- if (asBoolean) {
|
||||
- path.setDebug(this.openSet.getHeap(), set1.toArray(Node[]::new), set);
|
||||
+ // Paper start - Perf: remove streams and optimize collection
|
||||
+ Path best = null;
|
||||
+ boolean entryListIsEmpty = entryList.isEmpty();
|
||||
@ -125,11 +111,24 @@ index 1a9d8e6dd55ce30aca1c65b32417d61fd0a10f84..f90c2bb95e304bd6b9b8539d89f38d2d
|
||||
+ Path path = this.reconstructPath(entry.getKey().getBestNode(), entry.getValue(), !entryListIsEmpty);
|
||||
+ if (best == null || comparator.compare(path, best) < 0) {
|
||||
+ best = path;
|
||||
+ }
|
||||
+ }
|
||||
profilerFiller.pop();
|
||||
- if (optional.isEmpty()) {
|
||||
- return null;
|
||||
- } else {
|
||||
- Path path = optional.get();
|
||||
- if (asBoolean) {
|
||||
- path.setDebug(this.openSet.getHeap(), set1.toArray(Node[]::new), set);
|
||||
+ if(asBoolean && best != null) {
|
||||
+ Set<Target> set = Sets.newHashSet();
|
||||
+ for(Map.Entry<Target, BlockPos> entry : positions) {
|
||||
+ set.add(entry.getKey());
|
||||
}
|
||||
-
|
||||
- return path;
|
||||
+ best.setDebug(this.openSet.getHeap(), set1.toArray(Node[]::new), set);
|
||||
}
|
||||
+ profilerFiller.pop();
|
||||
+ return best;
|
||||
+ // Paper end - Perf: remove streams and optimize collection
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.StreamSupport;
|
||||
import net.kyori.adventure.bossbar.BossBar;
|
||||
import net.kyori.adventure.inventory.Book;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
@ -41,7 +40,6 @@ import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.locale.Language;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@ -55,13 +53,10 @@ import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.network.Filterable;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.BossEvent;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.WrittenBookContent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||
@ -333,28 +328,6 @@ public final class PaperAdventure {
|
||||
}
|
||||
}
|
||||
|
||||
// Book
|
||||
|
||||
public static ItemStack asItemStack(final Book book, final Locale locale) {
|
||||
final ItemStack item = new ItemStack(net.minecraft.world.item.Items.WRITTEN_BOOK, 1);
|
||||
item.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(
|
||||
Filterable.passThrough(validateField(asPlain(book.title(), locale), WrittenBookContent.TITLE_MAX_LENGTH, "title")),
|
||||
asPlain(book.author(), locale),
|
||||
0,
|
||||
book.pages().stream().map(c -> Filterable.passThrough(PaperAdventure.asVanilla(c))).toList(), // TODO should we validate length?
|
||||
false
|
||||
));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static String validateField(final String content, final int length, final String name) {
|
||||
final int actual = content.length();
|
||||
if (actual > length) {
|
||||
throw new IllegalArgumentException("Field '" + name + "' has a maximum length of " + length + " but was passed '" + content + "', which was " + actual + " characters long.");
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// Sounds
|
||||
|
||||
public static SoundSource asVanilla(final Sound.Source source) {
|
||||
|
||||
@ -11,6 +11,8 @@ import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.FeatureHooks;
|
||||
import io.papermc.paper.connection.PlayerGameConnection;
|
||||
import io.papermc.paper.connection.PluginMessageBridgeImpl;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import io.papermc.paper.datacomponent.item.WrittenBookContent;
|
||||
import io.papermc.paper.dialog.Dialog;
|
||||
import io.papermc.paper.dialog.PaperDialog;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
@ -48,6 +50,7 @@ import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import net.kyori.adventure.dialog.DialogLike;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.inventory.Book;
|
||||
import net.kyori.adventure.pointer.PointersSupplier;
|
||||
import net.kyori.adventure.util.TriState;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
@ -69,6 +72,7 @@ import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundBundlePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
||||
@ -76,6 +80,7 @@ import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenBookPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
@ -90,6 +95,7 @@ import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPac
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetHealthPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPlayerInventoryPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
|
||||
@ -110,6 +116,7 @@ import net.minecraft.server.permissions.PermissionLevel;
|
||||
import net.minecraft.server.players.UserWhiteListEntry;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntitySpawnReason;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
@ -203,6 +210,7 @@ import org.bukkit.event.player.PlayerShowEntityEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.InventoryView.Property;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.ItemType;
|
||||
import org.bukkit.map.MapCursor;
|
||||
import org.bukkit.map.MapView;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
@ -2711,17 +2719,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
|
||||
this.server.getServer().getCommands().sendCommands(this.getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openBook(ItemStack book) {
|
||||
Preconditions.checkArgument(book != null, "ItemStack cannot be null");
|
||||
Preconditions.checkArgument(book.getType() == Material.WRITTEN_BOOK, "ItemStack Material (%s) must be Material.WRITTEN_BOOK", book.getType());
|
||||
|
||||
ItemStack hand = this.getInventory().getItemInMainHand();
|
||||
this.getInventory().setItemInMainHand(book);
|
||||
this.getHandle().openItemGui(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(book), net.minecraft.world.InteractionHand.MAIN_HAND);
|
||||
this.getInventory().setItemInMainHand(hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openSign(@NonNull Sign sign, @NonNull Side side) {
|
||||
CraftSign.openSign(sign, this, side);
|
||||
@ -2960,17 +2957,30 @@ public class CraftPlayer extends CraftHumanEntity implements Player, PluginMessa
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openBook(final net.kyori.adventure.inventory.Book book) {
|
||||
final java.util.Locale locale = this.getHandle().adventure$locale;
|
||||
final net.minecraft.world.item.ItemStack item = io.papermc.paper.adventure.PaperAdventure.asItemStack(book, locale);
|
||||
final ServerPlayer player = this.getHandle();
|
||||
final ServerGamePacketListenerImpl connection = player.connection;
|
||||
final net.minecraft.world.entity.player.Inventory inventory = player.getInventory();
|
||||
final int slot = inventory.getNonEquipmentItems().size() + inventory.getSelectedSlot();
|
||||
final int stateId = getHandle().containerMenu.getStateId();
|
||||
connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(0, stateId, slot, item));
|
||||
connection.send(new net.minecraft.network.protocol.game.ClientboundOpenBookPacket(net.minecraft.world.InteractionHand.MAIN_HAND));
|
||||
connection.send(new net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket(0, stateId, slot, inventory.getSelectedItem()));
|
||||
public void openBook(ItemStack book) {
|
||||
Preconditions.checkArgument(book != null, "ItemStack cannot be null");
|
||||
Preconditions.checkArgument(book.hasData(DataComponentTypes.WRITTEN_BOOK_CONTENT), "ItemStack must have a 'written_book_content' component");
|
||||
|
||||
final ItemStack previousItem = this.getInventory().getItemInMainHand();
|
||||
this.getInventory().setItemInMainHand(book);
|
||||
this.getHandle().openItemGui(CraftItemStack.asNMSCopy(book), InteractionHand.MAIN_HAND);
|
||||
this.getInventory().setItemInMainHand(previousItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openBook(final Book book) {
|
||||
final ItemStack mutatedItem = ItemType.WRITTEN_BOOK.createItemStack(); // dummy item
|
||||
mutatedItem.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, WrittenBookContent.writtenBookContent("", "").addPages(book.pages()));
|
||||
|
||||
final net.minecraft.world.item.ItemStack selectedItem = this.getHandle().getInventory().getSelectedItem();
|
||||
final int slot = this.getHandle().getInventory().getSelectedSlot();
|
||||
this.getHandle().connection.send(new ClientboundBundlePacket(
|
||||
List.of(
|
||||
new ClientboundSetPlayerInventoryPacket(slot, CraftItemStack.unwrap(mutatedItem)),
|
||||
new ClientboundOpenBookPacket(InteractionHand.MAIN_HAND),
|
||||
new ClientboundSetPlayerInventoryPacket(slot, selectedItem)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
Reference in New Issue
Block a user