Merge remote-tracking branch 'upstream/main'

This commit is contained in:
2025-06-26 22:39:23 +02:00
67 changed files with 1021 additions and 521 deletions

View File

@ -11,7 +11,7 @@ java {
val annotationsVersion = "26.0.2"
// Keep in sync with paper-server adventure-text-serializer-ansi dep
val adventureVersion = "4.21.0"
val adventureVersion = "4.23.0"
val bungeeCordChatVersion = "1.21-R0.2-deprecated+build.21"
val slf4jVersion = "2.0.16"
val log4jVersion = "2.24.1"

View File

@ -1,5 +1,6 @@
package io.papermc.paper;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.world.damagesource.CombatEntry;
import io.papermc.paper.world.damagesource.FallLocationType;
import net.kyori.adventure.util.Services;
@ -11,6 +12,8 @@ import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.function.Predicate;
/**
* Static bridge to the server internals.
* <p>
@ -73,5 +76,15 @@ public interface InternalAPIBridge {
* @return combat entry
*/
CombatEntry createCombatEntry(DamageSource damageSource, float damage, @Nullable FallLocationType fallLocationType, float fallDistance);
/**
* Causes this predicate to be considered restricted.
* Applying this to a command node prevents this command from being executed from an
* unattended context, such as click events.
*
* @param predicate wrapped predicate
* @return wrapped predicate
*/
Predicate<CommandSourceStack> restricted(Predicate<CommandSourceStack> predicate);
}

View File

@ -5,6 +5,7 @@ import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.InternalAPIBridge;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
import io.papermc.paper.plugin.configuration.PluginMeta;
@ -13,6 +14,7 @@ import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.function.Predicate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
@ -85,6 +87,22 @@ public interface Commands extends Registrar {
return RequiredArgumentBuilder.argument(name, argumentType);
}
/**
* Creates a restricted {@link Predicate} that wraps the given predicate.
* <p>
* A restricted predicate prevents execution in unattended contexts, such as from
* chat click events. A warning is shown on the client before executing the command.
* <p>
* This is used by vanilla to prevent invocation of sensitive commands (like op) from
* players without their knowledge.
*
* @param predicate the original predicate to wrap
* @return a new predicate with restricted execution behavior
*/
static Predicate<CommandSourceStack> restricted(final Predicate<CommandSourceStack> predicate) {
return InternalAPIBridge.get().restricted(predicate);
}
/**
* Gets the underlying {@link CommandDispatcher}.
*

View File

@ -4,6 +4,8 @@ import com.destroystokyo.paper.util.SneakyThrow;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InaccessibleObjectException;
/**
* An <b>internal</b> utility type that holds logic for loading a provider-like type from a classloaders.
@ -56,7 +58,14 @@ public final class ProviderUtil {
throw new ClassCastException("class '%s' does not extend '%s'".formatted(clazz, classType));
}
clazzInstance = pluginClass.getDeclaredConstructor().newInstance();
final Constructor<? extends T> constructor = pluginClass.getDeclaredConstructor();
try {
constructor.setAccessible(true); // Allow non-public constructors
} catch (final InaccessibleObjectException | SecurityException ex) {
throw new RuntimeException("Inaccessible constructor");
}
clazzInstance = constructor.newInstance();
} catch (final IllegalAccessException exception) {
throw new RuntimeException("No public constructor");
} catch (final InstantiationException exception) {

View File

@ -76,16 +76,14 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
*/
RegistryKey<PotionEffectType> MOB_EFFECT = create("mob_effect");
/**
* @apiNote DO NOT USE
* Built-in registry for block types.
* @see io.papermc.paper.registry.keys.BlockTypeKeys
*/
@ApiStatus.Internal
RegistryKey<BlockType> BLOCK = create("block");
/**
* @apiNote use preferably only in the context of registry entries.
* @see io.papermc.paper.registry.data
* Built-in registry for item types.
* @see io.papermc.paper.registry.keys.ItemTypeKeys
*/
@ApiStatus.Experimental // Paper - already required for registry builders
RegistryKey<ItemType> ITEM = create("item");
/**
* Built-in registry for villager professions.

View File

@ -37,7 +37,7 @@ public enum SoundCategory implements Sound.Source.Provider {
case PLAYERS -> Sound.Source.PLAYER;
case AMBIENT -> Sound.Source.AMBIENT;
case VOICE -> Sound.Source.VOICE;
case UI -> throw new UnsupportedOperationException("Waiting on adventure release for the UI sound source"); // todo adventure
case UI -> Sound.Source.UI;
};
}
}

View File

@ -2,9 +2,10 @@ package org.bukkit.block;
import java.util.Collection;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Translatable;
import org.bukkit.World;
@ -82,7 +83,6 @@ import org.bukkit.block.data.type.MangrovePropagule;
import org.bukkit.block.data.type.MossyCarpet;
import org.bukkit.block.data.type.NoteBlock;
import org.bukkit.block.data.type.Observer;
import org.bukkit.block.data.type.PinkPetals;
import org.bukkit.block.data.type.Piston;
import org.bukkit.block.data.type.PistonHead;
import org.bukkit.block.data.type.PitcherCrop;
@ -122,18 +122,14 @@ import org.bukkit.block.data.type.WallHangingSign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.block.data.type.WallSkull;
import org.bukkit.inventory.ItemType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* While this API is in a public interface, it is not intended for use by
* plugins until further notice. The purpose of these types is to make
* {@link Material} more maintenance friendly, but will in due time be the
* official replacement for the aforementioned enum. Entirely incompatible
* changes may occur. Do not use this API in plugins.
* Represents a block type.
*/
@org.jetbrains.annotations.ApiStatus.Experimental // Paper - data component API - already required for data component API
@NullMarked
public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API
/**
@ -150,7 +146,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return the BlockData class of this BlockType
*/
@NotNull
@Override
Class<B> getBlockDataClass();
@ -161,7 +156,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @param consumer consumer to run on new instance before returning
* @return new data instance
*/
@NotNull
B createBlockData(@Nullable Consumer<? super B> consumer);
/**
@ -170,7 +164,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return new data instance
*/
@NotNull
@Override
B createBlockData();
@ -181,7 +174,7 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @return new block data collection
*/
@Override
@Unmodifiable @NotNull Collection<B> createBlockDataStates();
@Unmodifiable Collection<B> createBlockDataStates();
/**
* Creates a new {@link BlockData} instance for this block type, with all
@ -192,7 +185,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @return new data instance
* @throws IllegalArgumentException if the specified data is not valid
*/
@NotNull
B createBlockData(@Nullable String data);
}
@ -2411,10 +2403,10 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
// End generate - BlockType
//</editor-fold>
@NotNull
private static <B extends BlockType> B getBlockType(@NotNull String key) {
@SuppressWarnings("unchecked")
private static <B extends BlockType> B getBlockType(@KeyPattern.Value final String key) {
// Cast instead of using BlockType#typed, since block type can be a mock during testing and would return null
return (B) Registry.BLOCK.getOrThrow(NamespacedKey.minecraft(key));
return (B) Registry.BLOCK.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key));
}
/**
@ -2422,7 +2414,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return the typed block type.
*/
@NotNull
BlockType.Typed<BlockData> typed();
/**
@ -2432,8 +2423,7 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @param <B> the generic type of the block data to type this block type with.
* @return the typed block type.
*/
@NotNull
<B extends BlockData> BlockType.Typed<B> typed(@NotNull Class<B> blockDataType);
<B extends BlockData> BlockType.Typed<B> typed(Class<B> blockDataType);
/**
* Returns true if this BlockType has a corresponding {@link ItemType}.
@ -2447,12 +2437,14 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* Returns the corresponding {@link ItemType} for the given BlockType.
* <p>
* If there is no corresponding {@link ItemType} an error will be thrown.
* <p>This is <b>NOT</b> the same as the {@link ItemType} with the same key,
* but instead is the item associated with this block if this block
* can be represented with an item.</p>
*
* @return the corresponding ItemType
* @see #hasItemType()
* @see BlockData#getPlacementMaterial()
*/
@NotNull
ItemType getItemType();
/**
@ -2460,7 +2452,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return the BlockData class of this BlockType
*/
@NotNull
Class<? extends BlockData> getBlockDataClass();
/**
@ -2469,7 +2460,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return new data instance
*/
@NotNull
BlockData createBlockData();
/**
@ -2478,7 +2468,7 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
*
* @return new block data collection
*/
@Unmodifiable @NotNull Collection<? extends BlockData> createBlockDataStates();
@Unmodifiable Collection<? extends BlockData> createBlockDataStates();
/**
* Creates a new {@link BlockData} instance for this block type, with all
@ -2489,7 +2479,6 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @return new data instance
* @throws IllegalArgumentException if the specified data is not valid
*/
@NotNull
BlockData createBlockData(@Nullable String data);
/**
@ -2607,7 +2596,7 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @deprecated use {@link io.papermc.paper.world.flag.FeatureFlagSetHolder#isEnabled(io.papermc.paper.world.flag.FeatureDependant)}
*/
@Deprecated(forRemoval = true, since = "1.21.1") // Paper
boolean isEnabledByFeature(@NotNull World world);
boolean isEnabledByFeature(World world);
/**
* Tries to convert this BlockType into a Material
@ -2619,21 +2608,17 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
@Deprecated(since = "1.20.6")
Material asMaterial();
// Paper start - add Translatable
/**
* @deprecated use {@link #translationKey()} and {@link net.kyori.adventure.text.Component#translatable(net.kyori.adventure.translation.Translatable)}
*/
@Deprecated(forRemoval = true)
@Override
@NotNull String getTranslationKey();
// Paper end - add Translatable
String getTranslationKey();
// Paper start - hasCollision API
/**
* Checks if this block type has collision.
* <p>
* @return false if this block never has collision, true if it <b>might</b> have collision
*/
boolean hasCollision();
// Paper end - hasCollision API
}

View File

@ -1,10 +1,13 @@
package org.bukkit.inventory;
import com.google.common.collect.Multimap;
import io.papermc.paper.datacomponent.DataComponentType;
import java.util.Set;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Translatable;
import org.bukkit.World;
@ -36,17 +39,14 @@ import org.bukkit.inventory.meta.SpawnEggMeta;
import org.bukkit.inventory.meta.SuspiciousStewMeta;
import org.bukkit.inventory.meta.TropicalFishBucketMeta;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* While this API is in a public interface, it is not intended for use by
* plugins until further notice. The purpose of these types is to make
* {@link Material} more maintenance friendly, but will in due time be the
* official replacement for the aforementioned enum. Entirely incompatible
* changes may occur. Do not use this API in plugins.
* Represents an item type.
*/
@ApiStatus.Experimental // Paper - already required for registry builders
@NullMarked
public interface ItemType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add Translatable & feature flag API
/**
@ -54,7 +54,11 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* at compile time.
*
* @param <M> the generic type of the item meta that represents the item type.
* @apiNote Do not use methods exclusive to this interface unless you are
* fine with them being possibly removed in the future.
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
interface Typed<M extends ItemMeta> extends ItemType {
/**
@ -63,7 +67,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return the ItemMeta class of this ItemType
*/
@Override
@NotNull
@ApiStatus.Experimental
Class<M> getItemMetaClass();
/**
@ -73,7 +77,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* May be null if no intent exists to mutate the item meta at this point.
* @return the created and configured item stack.
*/
@NotNull
@ApiStatus.Experimental
ItemStack createItemStack(@Nullable Consumer<? super M> metaConfigurator);
/**
@ -84,7 +88,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* May be null if no intent exists to mutate the item meta at this point.
* @return the created and configured item stack.
*/
@NotNull
@ApiStatus.Experimental
ItemStack createItemStack(int amount, @Nullable Consumer<? super M> metaConfigurator);
}
@ -2923,18 +2927,19 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
// End generate - ItemType
//</editor-fold>
@NotNull
private static <M extends ItemType> M getItemType(@NotNull String key) {
@SuppressWarnings("unchecked")
private static <M extends ItemType> M getItemType(@KeyPattern.Value final String key) {
// Cast instead of using ItemType#typed, since item type can be a mock during testing and would return null
return (M) Registry.ITEM.getOrThrow(NamespacedKey.minecraft(key));
return (M) Registry.ITEM.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key));
}
/**
* Yields this item type as a typed version of itself with a plain {@link ItemMeta} representing it.
*
* @return the typed item type.
* @apiNote The Typed interface is experimental and may be removed in future versions.
*/
@NotNull
@ApiStatus.Experimental
Typed<ItemMeta> typed();
/**
@ -2943,16 +2948,16 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @param itemMetaType the class type of the {@link ItemMeta} to type this {@link ItemType} with.
* @param <M> the generic type of the item meta to type this item type with.
* @return the typed item type.
* @apiNote The Typed interface is experimental and may be removed in future versions.
*/
@NotNull
<M extends ItemMeta> Typed<M> typed(@NotNull Class<M> itemMetaType);
@ApiStatus.Experimental
<M extends ItemMeta> Typed<M> typed(Class<M> itemMetaType);
/**
* Constructs a new itemstack with this item type that has the amount 1.
*
* @return the constructed item stack.
*/
@NotNull
ItemStack createItemStack();
/**
@ -2961,7 +2966,6 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @param amount the amount of the item stack.
* @return the constructed item stack.
*/
@NotNull
ItemStack createItemStack(int amount);
/**
@ -2976,11 +2980,12 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* Returns the corresponding {@link BlockType} for the given ItemType.
* <p>
* If there is no corresponding {@link BlockType} an error will be thrown.
* <p>This is <b>NOT</b> the same as the {@link BlockType} with the same key,
* but instead is the block associated with this item if this item represents a block.</p>
*
* @return the corresponding BlockType
* @see #hasBlockType()
*/
@NotNull
BlockType getBlockType();
/**
@ -2988,7 +2993,6 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
*
* @return the ItemMeta class of this ItemType
*/
@NotNull
Class<? extends ItemMeta> getItemMetaClass();
/**
@ -3064,20 +3068,8 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
*
* @return the item left behind when crafting, or null if nothing is.
*/
@Nullable
ItemType getCraftingRemainingItem();
@Nullable ItemType getCraftingRemainingItem();
// /**
// * Get the best suitable slot for this item type.
// *
// * For most items this will be {@link EquipmentSlot#HAND}.
// *
// * @return the best EquipmentSlot for this item type
// */
// @NotNull
// EquipmentSlot getEquipmentSlot();
// Paper start - improve default item attribute API
/**
* Return an immutable copy of all default {@link Attribute}s and their
* {@link AttributeModifier}s.
@ -3089,8 +3081,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return the immutable {@link Multimap} with the respective default
* Attributes and modifiers, or an empty map if no attributes are set.
*/
@NotNull @org.jetbrains.annotations.Unmodifiable Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers();
// Paper end - improve default item attribute API
@Unmodifiable Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers();
/**
* Return an immutable copy of all default {@link Attribute}s and their
@ -3103,8 +3094,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return the immutable {@link Multimap} with the respective default
* Attributes and modifiers, or an empty map if no attributes are set.
*/
@NotNull
Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(@NotNull EquipmentSlot slot);
@Unmodifiable Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(EquipmentSlot slot);
/**
* Get the {@link CreativeCategory} to which this item type belongs.
@ -3112,9 +3102,8 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return the creative category. null if does not belong to a category
<!-- * @deprecated use #getCreativeCategories() -->
*/
@Nullable
@Deprecated(since = "1.20.6", forRemoval = true)
CreativeCategory getCreativeCategory();
@Nullable CreativeCategory getCreativeCategory();
/**
* Gets if the ItemType is enabled by the features in a world.
@ -3124,7 +3113,7 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @deprecated use {@link io.papermc.paper.world.flag.FeatureFlagSetHolder#isEnabled(io.papermc.paper.world.flag.FeatureDependant)}
*/
@Deprecated(forRemoval = true, since = "1.21.1") // Paper
boolean isEnabledByFeature(@NotNull World world);
boolean isEnabledByFeature(World world);
/**
* Tries to convert this ItemType into a Material
@ -3132,38 +3121,33 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return the converted Material or null
* @deprecated only for internal use
*/
@Nullable
@Deprecated(since = "1.20.6")
Material asMaterial();
@Nullable Material asMaterial();
// Paper start - add Translatable
/**
* @deprecated use {@link #translationKey()} and {@link net.kyori.adventure.text.Component#translatable(net.kyori.adventure.translation.Translatable)}
*/
@Deprecated(forRemoval = true)
@Override
@NotNull String getTranslationKey();
// Paper end - add Translatable
String getTranslationKey();
// Paper start - expand ItemRarity API
/**
* Returns the item rarity for the item.
*
* @return the item rarity (or null if none is set)
*/
@Nullable ItemRarity getItemRarity();
// Paper end - expand ItemRarity API
// Paper start - data component API
/**
* Gets the default value of the data component type for this item type.
*
* @param type the data component type
* @param <T> the value type
* @return the default value or {@code null} if there is none
* @see #hasDefaultData(io.papermc.paper.datacomponent.DataComponentType) for DataComponentType.NonValued
* @see #hasDefaultData(DataComponentType) for DataComponentType.NonValued
*/
@org.jetbrains.annotations.ApiStatus.Experimental
@Nullable <T> T getDefaultData(io.papermc.paper.datacomponent.DataComponentType.@NotNull Valued<T> type);
@ApiStatus.Experimental
@Nullable <T> T getDefaultData(DataComponentType.Valued<T> type);
/**
* Checks if the data component type has a default value for this item type.
@ -3172,14 +3156,13 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
* @return {@code true} if there is a default value
*/
@org.jetbrains.annotations.ApiStatus.Experimental
boolean hasDefaultData(io.papermc.paper.datacomponent.@NotNull DataComponentType type);
boolean hasDefaultData(DataComponentType type);
/**
* Gets the default data component types for this item type.
*
* @return an immutable set of data component types
*/
@org.jetbrains.annotations.ApiStatus.Experimental
java.util.@org.jetbrains.annotations.Unmodifiable @NotNull Set<io.papermc.paper.datacomponent.DataComponentType> getDefaultDataTypes();
// Paper end - data component API
@ApiStatus.Experimental
@Unmodifiable Set<DataComponentType> getDefaultDataTypes();
}

View File

@ -29,7 +29,7 @@ public interface PluginManager extends io.papermc.paper.plugin.PermissionManager
/**
* Checks if the given plugin is loaded and returns it when applicable
* <p>
* Please note that the name of the plugin is case-sensitive
* Please note that the name of the plugin is case-insensitive
*
* @param name Name of the plugin to check
* @return Plugin if it exists, otherwise null
@ -48,7 +48,7 @@ public interface PluginManager extends io.papermc.paper.plugin.PermissionManager
/**
* Checks if the given plugin is enabled or not
* <p>
* Please note that the name of the plugin is case-sensitive.
* Please note that the name of the plugin is case-insensitive.
*
* @param name Name of the plugin to check
* @return true if the plugin is enabled, otherwise false

View File

@ -464,7 +464,7 @@ public final class SimplePluginManager implements PluginManager {
/**
* Checks if the given plugin is loaded and returns it when applicable
* <p>
* Please note that the name of the plugin is case-sensitive
* Please note that the name of the plugin is case-insensitive
*
* @param name Name of the plugin to check
* @return Plugin if it exists, otherwise null
@ -486,7 +486,7 @@ public final class SimplePluginManager implements PluginManager {
/**
* Checks if the given plugin is enabled or not
* <p>
* Please note that the name of the plugin is case-sensitive.
* Please note that the name of the plugin is case-insensitive.
*
* @param name Name of the plugin to check
* @return true if the plugin is enabled, otherwise false

View File

@ -6,6 +6,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
@ -91,13 +92,20 @@ public final class PluginClassLoader extends URLClassLoader implements io.paperm
try {
pluginConstructor = pluginClass.getDeclaredConstructor();
} catch (NoSuchMethodException ex) {
throw new InvalidPluginException("main class `" + description.getMain() + "' must have a public no-args constructor", ex);
throw new InvalidPluginException("main class `" + description.getMain() + "' must have a no-args constructor", ex);
}
try {
// Support non-public constructors
pluginConstructor.setAccessible(true);
} catch (InaccessibleObjectException | SecurityException ex) {
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor inaccessible", ex);
}
try {
plugin = pluginConstructor.newInstance();
} catch (IllegalAccessException ex) {
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor must be public", ex);
throw new InvalidPluginException("main class `" + description.getMain() + "' constructor inaccessible", ex);
} catch (InstantiationException ex) {
throw new InvalidPluginException("main class `" + description.getMain() + "' must not be abstract", ex);
} catch (IllegalArgumentException ex) {

View File

@ -8,13 +8,14 @@ import org.bukkit.block.spawner.SpawnerEntry;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.minecart.SpawnerMinecart;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Represents a basic entity spawner. <br>
* May be a {@link SpawnerMinecart}, {@link CreatureSpawner} or {@link TrialSpawnerConfiguration}.
*/
@NullMarked
public interface BaseSpawner {
/**
@ -23,7 +24,7 @@ public interface BaseSpawner {
* @return The creature type or null if it not set.
*/
@Nullable
public EntityType getSpawnedType();
EntityType getSpawnedType();
/**
* Set the spawner's creature type. <br>
@ -31,7 +32,7 @@ public interface BaseSpawner {
*
* @param creatureType The creature type or null to clear.
*/
public void setSpawnedType(@Nullable EntityType creatureType);
void setSpawnedType(@Nullable EntityType creatureType);
/**
* Get the spawner's delay.
@ -40,14 +41,14 @@ public interface BaseSpawner {
*
* @return The delay.
*/
public int getDelay();
int getDelay();
/**
* Set the spawner's delay.
*
* @param delay The delay.
*/
public void setDelay(int delay);
void setDelay(int delay);
/**
* Get the maximum distance(squared) a player can be in order for this
@ -61,7 +62,7 @@ public interface BaseSpawner {
* @return the maximum distance(squared) a player can be in order for this
* spawner to be active.
*/
public int getRequiredPlayerRange();
int getRequiredPlayerRange();
/**
* Set the maximum distance (squared) a player can be in order for this
@ -73,7 +74,7 @@ public interface BaseSpawner {
* @param requiredPlayerRange the maximum distance (squared) a player can be
* in order for this spawner to be active.
*/
public void setRequiredPlayerRange(int requiredPlayerRange);
void setRequiredPlayerRange(int requiredPlayerRange);
/**
* Get the radius around which the spawner will attempt to spawn mobs in.
@ -89,7 +90,7 @@ public interface BaseSpawner {
*
* @return the spawn range
*/
public int getSpawnRange();
int getSpawnRange();
/**
* Set the new spawn range.
@ -98,7 +99,7 @@ public interface BaseSpawner {
* @param spawnRange the new spawn range
* @see #getSpawnRange()
*/
public void setSpawnRange(int spawnRange);
void setSpawnRange(int spawnRange);
/**
* Gets the {@link EntitySnapshot} that will be spawned by this spawner or null
@ -111,7 +112,7 @@ public interface BaseSpawner {
* spawner.
*/
@Nullable
public EntitySnapshot getSpawnedEntity();
EntitySnapshot getSpawnedEntity();
/**
* Sets the entity that will be spawned by this spawner. <br>
@ -123,7 +124,7 @@ public interface BaseSpawner {
*
* @param snapshot the entity snapshot or null to clear
*/
public void setSpawnedEntity(@Nullable EntitySnapshot snapshot);
void setSpawnedEntity(@Nullable EntitySnapshot snapshot);
/**
* Sets the {@link SpawnerEntry} that will be spawned by this spawner. <br>
@ -132,14 +133,14 @@ public interface BaseSpawner {
*
* @param spawnerEntry the spawner entry to use
*/
public void setSpawnedEntity(@NotNull SpawnerEntry spawnerEntry);
void setSpawnedEntity(SpawnerEntry spawnerEntry);
/**
* Adds a new {@link EntitySnapshot} to the list of entities this spawner can
* spawn.
* <p>
* The weight will determine how often this entry is chosen to spawn, higher
* weighted entries will spawn more often than lower weighted ones. <br>
* weighted entries will spawn more often than lower-weighted ones. <br>
* The {@link SpawnRule} will determine under what conditions this entry can
* spawn, passing null will use the default conditions for the given entity.
*
@ -147,7 +148,7 @@ public interface BaseSpawner {
* @param weight the weight
* @param spawnRule the spawn rule for this entity, or null
*/
public void addPotentialSpawn(@NotNull EntitySnapshot snapshot, int weight, @Nullable SpawnRule spawnRule);
void addPotentialSpawn(EntitySnapshot snapshot, int weight, @Nullable SpawnRule spawnRule);
/**
* Adds a new {@link SpawnerEntry} to the list of entities this spawner can
@ -156,7 +157,7 @@ public interface BaseSpawner {
* @param spawnerEntry the spawner entry to use
* @see #addPotentialSpawn(EntitySnapshot, int, SpawnRule)
*/
public void addPotentialSpawn(@NotNull final SpawnerEntry spawnerEntry);
void addPotentialSpawn(final SpawnerEntry spawnerEntry);
/**
* Sets the list of {@link SpawnerEntry} this spawner can spawn. <br>
@ -165,7 +166,7 @@ public interface BaseSpawner {
*
* @param entries the list of entries
*/
public void setPotentialSpawns(@NotNull final Collection<SpawnerEntry> entries);
void setPotentialSpawns(final Collection<SpawnerEntry> entries);
/**
* Gets a list of potential spawns from this spawner or an empty list if no
@ -177,6 +178,5 @@ public interface BaseSpawner {
* entities have been assigned to this spawner
* @see #getSpawnedType()
*/
@NotNull
public List<SpawnerEntry> getPotentialSpawns();
List<SpawnerEntry> getPotentialSpawns();
}

View File

@ -3,11 +3,14 @@ package org.bukkit.spawner;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.minecart.SpawnerMinecart;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NullMarked;
/**
* Represents an entity spawner. <br>
* May be a {@link SpawnerMinecart} or a {@link CreatureSpawner}.
*/
@NullMarked
public interface Spawner extends BaseSpawner {
/**
@ -19,7 +22,7 @@ public interface Spawner extends BaseSpawner {
* @param delay The delay.
*/
@Override
public void setDelay(int delay);
void setDelay(int delay);
/**
* The minimum spawn delay amount (in ticks).
@ -32,7 +35,7 @@ public interface Spawner extends BaseSpawner {
*
* @return the minimum spawn delay amount
*/
public int getMinSpawnDelay();
int getMinSpawnDelay();
/**
* Set the minimum spawn delay amount (in ticks).
@ -40,7 +43,7 @@ public interface Spawner extends BaseSpawner {
* @param delay the minimum spawn delay amount
* @see #getMinSpawnDelay()
*/
public void setMinSpawnDelay(int delay);
void setMinSpawnDelay(int delay);
/**
* The maximum spawn delay amount (in ticks).
@ -56,7 +59,7 @@ public interface Spawner extends BaseSpawner {
*
* @return the maximum spawn delay amount
*/
public int getMaxSpawnDelay();
int getMaxSpawnDelay();
/**
* Set the maximum spawn delay amount (in ticks).
@ -67,7 +70,7 @@ public interface Spawner extends BaseSpawner {
* @param delay the new maximum spawn delay amount
* @see #getMaxSpawnDelay()
*/
public void setMaxSpawnDelay(int delay);
void setMaxSpawnDelay(int delay);
/**
* Get how many mobs attempt to spawn.
@ -76,27 +79,27 @@ public interface Spawner extends BaseSpawner {
*
* @return the current spawn count
*/
public int getSpawnCount();
int getSpawnCount();
/**
* Set how many mobs attempt to spawn.
*
* @param spawnCount the new spawn count
*/
public void setSpawnCount(int spawnCount);
void setSpawnCount(int spawnCount);
/**
* Set the new maximum amount of similar entities that are allowed to be
* within spawning range of this spawner.
* Get the maximum number of similar entities that are allowed to be
* within the spawning range of this spawner.
* <br>
* If more than the maximum number of entities are within range, the spawner
* will not spawn and try again with a new {@link #getDelay()}.
* <br>
* Default value is 16.
* Default value is 6.
*
* @return the maximum number of nearby, similar, entities
*/
public int getMaxNearbyEntities();
int getMaxNearbyEntities();
/**
* Set the maximum number of similar entities that are allowed to be within
@ -106,20 +109,19 @@ public interface Spawner extends BaseSpawner {
*
* @param maxNearbyEntities the maximum number of nearby, similar, entities
*/
public void setMaxNearbyEntities(int maxNearbyEntities);
void setMaxNearbyEntities(int maxNearbyEntities);
// Paper start
/**
* Check if spawner is activated (a player is close enough)
*
* @return True if a player is close enough to activate it
*/
public boolean isActivated();
boolean isActivated();
/**
* Resets the spawn delay timer within the min/max range
*/
public void resetTimer();
void resetTimer();
/**
* Sets the {@link EntityType} to {@link EntityType#ITEM} and sets the data to the given
@ -128,9 +130,8 @@ public interface Spawner extends BaseSpawner {
* {@link #setSpawnCount(int)} does not dictate the amount of items in the stack spawned, but rather how many
* stacks should be spawned.
*
* @param itemStack The item to spawn. Must not {@link org.bukkit.Material#isAir be air}.
* @param itemStack The item to spawn. Must not {@link ItemStack#isEmpty() be empty}.
* @see #setSpawnedType(EntityType)
*/
void setSpawnedItem(org.bukkit.inventory.@org.jetbrains.annotations.NotNull ItemStack itemStack);
// Paper end
void setSpawnedItem(ItemStack itemStack);
}

View File

@ -1,3 +1,4 @@
import io.papermc.fill.model.BuildChannel
import io.papermc.paperweight.attribute.DevBundleOutput
import io.papermc.paperweight.util.*
import io.papermc.paperweight.util.data.FileEntry
@ -10,6 +11,7 @@ plugins {
`maven-publish`
idea
id("io.papermc.paperweight.core")
id("io.papermc.fill.gradle") version "1.0.3"
}
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
@ -132,7 +134,7 @@ dependencies {
implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+
implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21
implementation("net.minecrell:terminalconsoleappender:1.3.0")
implementation("net.kyori:adventure-text-serializer-ansi:4.21.0") // Keep in sync with adventureVersion from Paper-API build file
implementation("net.kyori:adventure-text-serializer-ansi:4.23.0") // Keep in sync with adventureVersion from Paper-API build file
runtimeConfiguration(sourceSets.main.map { it.runtimeClasspath })
/*
@ -372,3 +374,20 @@ tasks.registerRunTask("runReobfPaperclip") {
classpath(tasks.createReobfPaperclipJar.flatMap { it.outputZip })
mainClass.set(null as String?)
}
fill {
project("paper")
versionFamily(paperweight.minecraftVersion.map { it.split(".", "-").takeWhile { part -> part.toIntOrNull() != null }.take(2).joinToString(".") })
version(paperweight.minecraftVersion)
build {
channel = BuildChannel.STABLE
downloads {
register("server:default") {
file = tasks.createMojmapPaperclipJar.flatMap { it.outputZip }
nameResolver.set { project, _, version, build -> "$project-$version-$build.jar" }
}
}
}
}

View File

@ -354,7 +354,7 @@ index 0000000000000000000000000000000000000000..ae2bb9a73106febfe5f0d090abd4252b
+ }
+}
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 7b5ed00c9b2f22af5cbc44171192d674936dc7d7..5fe3c9a159908785e08fa874982bc1a82283bb1d 100644
index d51645b115780dac9ff6010806e8bd62dedc8e9f..3faf1b5556c55f3468182f75b2dbff4aaa3aae3a 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList;
@ -484,7 +484,7 @@ index c70a58f5f633fa8e255f74c42f5e87c96b7b013a..ec20a5a6d7c8f65abda528fec36bec7b
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index f961540a00bfb5e1c8eb0e739f8ae535e9eee8f3..7453ddb09f349b7836f966573e4933646a75cba6 100644
index 75f81a6bc156a6455a616b8de0d7701fd2255a2d..b3d951670b3a097d04cfe347d7df496b1d0a0e09 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -409,6 +409,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@ -661,10 +661,10 @@ index 3f780276be6766ef253c50212e06fd93a96b0caa..7e70c7bee633c54497d1cd2854dd60f4
}
}
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index 548d7c8dc517da6c4db86b11f579ae63e6db56cf..a29860af4c37b2b45df49f9ba18f7e38921dfb02 100644
index fca31bbab8e7830933ceffcf992ff56ccc84414c..51804b611f469f2ab53e455e8c633b867b00cc88 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -121,6 +121,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
@@ -129,6 +129,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
return 0.04;
}
@ -695,7 +695,7 @@ index 548d7c8dc517da6c4db86b11f579ae63e6db56cf..a29860af4c37b2b45df49f9ba18f7e38
public void tick() {
if (this.getItem().isEmpty()) {
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index b0607f4a9b35570b319423c7876bb904d5154e8e..98c8653647dc52059d8becfe38a74d4e62edf08f 100644
index ef8347329b440833b45a54be2b6e4204ac0a425e..43f16df230f87a43e249a58fc10ef2da517f22ee 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -269,11 +269,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
@ -838,7 +838,7 @@ index 52acc72841f0c6980f5f3f8ef21d0b29dd472ce3..41a6ec508a10a49a37539d2f10171d15
+
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index d26e4d85d8fd8bd4f0c7de30b50a2ce370b37bf5..bab28e7afb7b1249d40631aabff16fc18cf95ea0 100644
index 06069d3ac598f5f12feab038de4f1199794298f6..980eaba27ce2616c1573a4760cf4acc2dd251190 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -143,6 +143,12 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl

View File

@ -5,10 +5,10 @@ Subject: [PATCH] Anti-Xray
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index 55cc0dec4a88baea17f160e95d5d8316e0bb7a50..338dc0fb07cdba5f7350cca332fa3e942c622bfb 100644
index 811a8e5141f2061a185b53b63d951646141c0c7d..33d3eb510c5844e72bbc382bd24641aae080962d 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -40,20 +40,25 @@ public final class FeatureHooks {
@@ -45,20 +45,25 @@ public final class FeatureHooks {
}
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {

View File

@ -14,17 +14,17 @@ movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 7453ddb09f349b7836f966573e4933646a75cba6..58eda0d6426f30cda604f4120f1ddb012316c108 100644
index 23dfc87db1d5e90099270627197abc0f787a4393..27a01fd28ea565221768f31df02f0a2ddf242fce 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -229,6 +229,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// Paper end - Share random for entities to make them more random
@@ -230,6 +230,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
public @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
private volatile @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity; // Paper - Folia schedulers - volatile
+ public boolean collisionLoadChunks = false; // Paper
private @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
if (this.bukkitEntity == null) {
diff --git a/net/minecraft/world/level/BlockCollisions.java b/net/minecraft/world/level/BlockCollisions.java
index ed6e4f9fd0c7ad1219e66bc1cb4038191dd6edd8..45a20dbb935b12d429153463dba5d6fd3385dd7a 100644
--- a/net/minecraft/world/level/BlockCollisions.java

View File

@ -32481,7 +32481,7 @@ index 0000000000000000000000000000000000000000..b028017b9c44821a8a313a04e0b10f5d
+ }
+}
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
index 0c611598a6912b692a7639301811ee557e2304f1..6f65564f6a6e99d6549de9a23a031b1f4a8a9798 100644
index e4f0653c575c33b1ef8160b6c88e29ee9fb44508..b6a34796d33f1593301c3a67a013fa7e812cb329 100644
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
@@ -211,6 +211,43 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
@ -32542,7 +32542,7 @@ index 6536dc08c80170f5679acedd65cd2b9f6ad3fb3a..294cd15a796ad25823c8ccf98fbfae46
return structureTemplate.save(new CompoundTag());
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 794a01ee70a2a30d91550f5265f774ba73828cf9..c716521fb1497dc8a22d827ddb50fc1cc21a05f4 100644
index 2d597e50dcd957bd566c4da384fac5f36b5362f7..75aba65cbe1a943f21c7464ff9465e64f63e8e5b 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@ -32585,10 +32585,10 @@ index 8c1417c659ea0e079e99b9bfa79e1cf6ba9b712b..a8a32edea080f32fd25c9e009d4efa41
if (stopBelowZero) {
chunkData.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index 6be673172548c1382c7402ec4e1ec6ef51f702d3..18750f1ea3101b6c0ab0b8e33c304eb7fa1ed04d 100644
index 6be673172548c1382c7402ec4e1ec6ef51f702d3..49be43ac896d60587511a97445c53c10c587a341 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -32,13 +32,30 @@ public class SimpleRegionStorage implements AutoCloseable {
@@ -32,13 +32,32 @@ public class SimpleRegionStorage implements AutoCloseable {
return this.worker.store(chunkPos, data);
}
@ -32609,7 +32609,9 @@ index 6be673172548c1382c7402ec4e1ec6ef51f702d3..18750f1ea3101b6c0ab0b8e33c304eb7
- return this.dataFixType.updateToCurrentVersion(this.fixerUpper, tag, dataVersion);
+ // Paper start - rewrite data conversion system
+ final int dataVer = NbtUtils.getDataVersion(tag, version);
+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), tag, dataVer, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
+ final CompoundTag ret = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), tag, dataVer, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
+ NbtUtils.addCurrentDataVersion(ret); // Fix MC-299110
+ return ret;
+ // Paper end - rewrite data conversion system
}

View File

@ -23061,7 +23061,7 @@ index 0000000000000000000000000000000000000000..f1f72a051083b61273202cb4e67ecb11
+ private SaveUtil() {}
+}
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab4e1512da 100644
index b1a7af2dd3a3e166b1e58125f13ce08b9ec6d9ff..df6fbb35e5023b42de0b97434712e04a6b3e66a3 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -1,6 +1,9 @@
@ -23074,8 +23074,13 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
@@ -31,12 +34,16 @@ import org.bukkit.World;
public final class FeatureHooks {
@@ -32,16 +35,20 @@ public final class FeatureHooks {
// this includes non-accessible entities
public static Iterable<Entity> getAllEntities(final net.minecraft.server.level.ServerLevel world) {
- return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter<Entity>)world.getEntities()).sectionStorage.getAllEntities();
+ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup)world.getEntities()).getAllMapped(); // Paper - rewrite chunk system
}
public static void setPlayerChunkUnloadDelay(final long ticks) {
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.setUnloadDelay(ticks); // Paper - rewrite chunk system
@ -23091,7 +23096,7 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
}
public static LevelChunkSection createSection(final Registry<Biome> biomeRegistry, final Level level, final ChunkPos chunkPos, final int chunkSection) {
@@ -62,111 +69,58 @@ public final class FeatureHooks {
@@ -67,111 +74,58 @@ public final class FeatureHooks {
}
public static Set<Long> getSentChunkKeys(final ServerPlayer player) {
@ -23224,7 +23229,7 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
org.bukkit.Chunk chunk = null;
for (net.minecraft.server.level.Ticket ticket : tickets) {
@@ -186,15 +140,15 @@ public final class FeatureHooks {
@@ -191,15 +145,15 @@ public final class FeatureHooks {
}
public static int getViewDistance(net.minecraft.server.level.ServerLevel world) {
@ -23243,7 +23248,7 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
}
public static void setViewDistance(net.minecraft.server.level.ServerLevel world, int distance) {
@@ -212,35 +166,31 @@ public final class FeatureHooks {
@@ -217,35 +171,31 @@ public final class FeatureHooks {
}
public static void setSendViewDistance(net.minecraft.server.level.ServerLevel world, int distance) {
@ -23286,6 +23291,7 @@ index 338dc0fb07cdba5f7350cca332fa3e942c622bfb..e1fe49e4bf014e2405708270efd81bab
}
}
\ No newline at end of file
diff --git a/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dca7afbd93cfbb8686f336fcd3b45dd01fba0fc
@ -23861,7 +23867,7 @@ index 46de98a6bbbae48c4837e1e588ba198a363d2dde..fd3553bdc1c3cdbf6aa3dc00e0a4987f
thread1 -> {
DedicatedServer dedicatedServer1 = new DedicatedServer(
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787fb963eef4 100644
index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a718d12b8 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2;
@ -23960,7 +23966,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(serverLevel.getWorld()));
}
@@ -844,6 +915,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -845,6 +916,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public abstract boolean shouldRconBroadcast();
public boolean saveAllChunks(boolean suppressLog, boolean flush, boolean forced) {
@ -23972,7 +23978,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
boolean flag = false;
for (ServerLevel serverLevel : this.getAllLevels()) {
@@ -851,7 +927,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -852,7 +928,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
LOGGER.info("Saving chunks for level '{}'/{}", serverLevel, serverLevel.dimension().location());
}
@ -23981,7 +23987,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
flag = true;
}
@@ -944,7 +1020,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -945,7 +1021,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@ -23990,7 +23996,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND;
for (ServerLevel serverLevelx : this.getAllLevels()) {
@@ -954,18 +1030,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -955,18 +1031,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.waitUntilNextTick();
}
@ -24016,7 +24022,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
this.isSaving = false;
this.resources.close();
@@ -985,6 +1057,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -986,6 +1058,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getProfileCache().save(false); // Paper - Perf: Async GameProfileCache saving
}
// Spigot end
@ -24031,7 +24037,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
// Paper start - Improved watchdog support - move final shutdown items here
Util.shutdownExecutors();
try {
@@ -1169,6 +1249,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1170,6 +1250,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
profilerFiller.push("tick");
this.tickFrame.start();
this.tickServer(flag ? () -> false : this::haveTime);
@ -24045,7 +24051,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
this.tickFrame.end();
profilerFiller.popPush("nextTickWait");
this.mayHaveDelayedTasks = true;
@@ -1339,6 +1426,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1340,6 +1427,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private boolean pollTaskInternal() {
if (super.pollTask()) {
@ -24053,7 +24059,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
return true;
} else {
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
@@ -2468,6 +2556,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2478,6 +2566,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@ -24067,7 +24073,7 @@ index c716521fb1497dc8a22d827ddb50fc1cc21a05f4..80442494db670fec34df310390ea787f
// CraftBukkit start
public boolean isDebugging() {
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 5db176be3bd31eb886a541eeaee922ee30ee9908..5fea5e2e9fc10d348fa3e65cd354ef6a4a717a4d 100644
index 4488d0a2f05ef07afab0f9a1483f54b21757b29e..98927d4a5fba2a0dcdb147ac10b82c3286ccdc6b 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -391,7 +391,33 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@ -27685,7 +27691,7 @@ index 49008b4cbaead8a66a93d2b0d4b50b335a6c3eed..f9c96bbdc54e68b9216b7f8662bfae03
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 69dbff3f62e3a9ba7090156380f842bb44deebf8..c0b74d408340101bc3aac4cb4b7232c5cc78b08a 100644
index 6f7f92cc43c56a7453b289f205502d089474ef6d..b390ba657b8b880e431c84e9dd948ac9c84af2fd 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -193,7 +193,7 @@ import net.minecraft.world.scores.Team;
@ -28722,7 +28728,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
+ // Paper end - block counting
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef1e3bfbcb 100644
index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbdea218c373 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@ -29430,7 +29436,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
if (!checkPosition(this, x, y, z)) {
return;
}
@@ -4828,6 +5135,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4818,6 +5125,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@Override
public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause
@ -29443,7 +29449,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
if (this.removalReason == null) {
@@ -4838,7 +5151,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4828,7 +5141,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.stopRiding();
}
@ -29452,7 +29458,7 @@ index fa2ffc44336c3ddfe576b202154f14ef91a301b9..7546ff4c5ffc62d93a3f874519db8fef
this.levelCallback.onRemove(removalReason);
this.onRemoval(removalReason);
// Paper start - Folia schedulers
@@ -4872,7 +5185,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -4862,7 +5175,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
public boolean shouldBeSaved() {
return (this.removalReason == null || this.removalReason.shouldSave())
&& !this.isPassenger()
@ -34311,7 +34317,7 @@ index 15417fab103feec3c1f7d5bd5b332e89d3ace3f5..2e6263d8b466e0f61bc72eb818044734
}
}
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index 18750f1ea3101b6c0ab0b8e33c304eb7fa1ed04d..bf708ff89ea1f2c7279e48c41c4f44abc77ceebb 100644
index 49be43ac896d60587511a97445c53c10c587a341..1b070cf7e3612dbcb170cf5d954eba5f5b3c777c 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -14,7 +14,7 @@ import net.minecraft.util.datafix.DataFixTypes;
@ -36675,10 +36681,10 @@ index c634d795644be86ad85395ffa39fbac33bf7418b..66d0a6390febe929ef774b0a78133290
for (SavedTick<T> savedTick : this.pendingTicks) {
diff --git a/net/minecraft/world/waypoints/WaypointTransmitter.java b/net/minecraft/world/waypoints/WaypointTransmitter.java
index b579839c03b371d408e3750ec09af7da1d7bc9a0..9b41c62afc861847571ad739d1dd848b8276230c 100644
index 47382efcd50f29601a6623876be50c4d047336c5..5d1c933dfa862d0733777d305563a89ea7827f07 100644
--- a/net/minecraft/world/waypoints/WaypointTransmitter.java
+++ b/net/minecraft/world/waypoints/WaypointTransmitter.java
@@ -31,7 +31,10 @@ public interface WaypointTransmitter extends Waypoint {
@@ -32,7 +32,10 @@ public interface WaypointTransmitter extends Waypoint {
}
static boolean isChunkVisible(ChunkPos pos, ServerPlayer player) {

View File

@ -5,10 +5,10 @@ Subject: [PATCH] Incremental chunk and player saving
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 80442494db670fec34df310390ea787fb963eef4..2dd512565ab901bf853f34b384155902b0fe8120 100644
index 338ef549efe82c250c74365c1c1071986920c8c9..39581095ccc69d113d954ed835bdfa32d25b5489 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -954,7 +954,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -955,7 +955,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
boolean var4;
try {
this.isSaving = true;
@ -17,7 +17,7 @@ index 80442494db670fec34df310390ea787fb963eef4..2dd512565ab901bf853f34b384155902
var4 = this.saveAllChunks(suppressLog, flush, forced);
} finally {
this.isSaving = false;
@@ -1533,9 +1533,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1534,9 +1534,29 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.ticksUntilAutosave--;

View File

@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
+ }
+}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 2dd512565ab901bf853f34b384155902b0fe8120..d6dcb6d146d89a8fb96e7c669e5deb802223abd6 100644
index cff9d761dfee8a90b19fb2f3e678f99a39fc000c..0a260fdf6b198a8ab52e60bf6db2fb5eab719c48 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1708,6 +1708,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1718,6 +1718,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
serverLevel.updateLagCompensationTick(); // Paper - lag compensation

View File

@ -0,0 +1,204 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 24 Jun 2025 03:41:38 -0700
Subject: [PATCH] Improve keepalive ping system
Send more keepalives, record all transactions within the last minute.
We send more keepalives so that the latency calculation is more
accurate. Since we send more keepalives, we track all pending
keepalives in case multiple end up in flight.
Additionally, replace the latency calculation with a true
average over the last 5 seconds of keepalive transactions.
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 53f038e1b5e7a13a08a0c925c8bd3f8a40868195..f3eca351021c37b64315872d075bd0a84aeee267 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -461,6 +461,70 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return this.viewDistanceHolder;
}
// Paper end - rewrite chunk system
+ // Paper start - improve keepalives
+ public long lastKeepAliveTx = System.nanoTime();
+ public static final record KeepAliveResponse(long txTimeNS, long rxTimeNS) {
+ public long latencyNS() {
+ return this.rxTimeNS - this.txTimeNS;
+ }
+ }
+ public static final record PendingKeepAlive(long txTimeNS, long challengeId) {}
+
+ public final ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<PendingKeepAlive> pendingKeepAlives = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>();
+
+ public final PingCalculator pingCalculator1m = new PingCalculator(java.util.concurrent.TimeUnit.MINUTES.toNanos(1L));
+ public final PingCalculator pingCalculator5s = new PingCalculator(java.util.concurrent.TimeUnit.SECONDS.toNanos(5L));
+
+ public static final class PingCalculator {
+
+ private final long intervalNS;
+ private final ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<KeepAliveResponse> responses = new ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue<>();
+
+ private long timeSumNS;
+ private int timeSumCount;
+ private volatile long lastAverageNS;
+
+ public PingCalculator(long intervalNS) {
+ this.intervalNS = intervalNS;
+ }
+
+ public void update(KeepAliveResponse response) {
+ long currTime = response.txTimeNS;
+
+ this.responses.add(response);
+
+ ++this.timeSumCount;
+ this.timeSumNS += response.latencyNS();
+
+ // remove out-of-window times
+ KeepAliveResponse removed;
+ while ((removed = this.responses.pollIf((ka) -> (currTime - ka.txTimeNS) > this.intervalNS)) != null) {
+ --this.timeSumCount;
+ this.timeSumNS -= removed.latencyNS();
+ }
+
+ this.lastAverageNS = this.timeSumNS / (long)this.timeSumCount;
+ }
+
+ public int getAvgLatencyMS() {
+ return (int)java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(this.getAvgLatencyNS());
+ }
+
+ public long getAvgLatencyNS() {
+ return this.lastAverageNS;
+ }
+
+ public it.unimi.dsi.fastutil.longs.LongArrayList getAllNS() {
+ it.unimi.dsi.fastutil.longs.LongArrayList ret = new it.unimi.dsi.fastutil.longs.LongArrayList();
+
+ for (KeepAliveResponse response : this.responses) {
+ ret.add(response.latencyNS());
+ }
+
+ return ret;
+ }
+ }
+ // Paper end - improve keepalives
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
super(level, gameProfile);
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 85e01c3a1536b41a0301a5a6506e058ff9633a4a..43f70a5561d6cc62aaeba6d1e39598ecb382e369 100644
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -38,12 +38,12 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
protected final MinecraftServer server;
public final Connection connection; // Paper
private final boolean transferred;
- private long keepAliveTime;
- private boolean keepAlivePending;
- private long keepAliveChallenge;
+ //private long keepAliveTime; // Paper - improve keepalives
+ //private boolean keepAlivePending; // Paper - improve keepalives
+ //private long keepAliveChallenge; // Paper - improve keepalives
private long closedListenerTime;
private boolean closed = false;
- private int latency;
+ private volatile int latency; // Paper - improve keepalives - make volatile
private volatile boolean suspendFlushingOnServerThread = false;
// CraftBukkit start
protected final net.minecraft.server.level.ServerPlayer player;
@@ -57,7 +57,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, net.minecraft.server.level.ServerPlayer player) { // CraftBukkit
this.server = server;
this.connection = connection;
- this.keepAliveTime = Util.getMillis();
+ //this.keepAliveTime = Util.getMillis(); // Paper - improve keepalives
this.latency = cookie.latency();
this.transferred = cookie.transferred();
// CraftBukkit start - add fields and methods
@@ -120,13 +120,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@Override
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
- if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) {
- int i = (int)(Util.getMillis() - this.keepAliveTime);
- this.latency = (this.latency * 3 + i) / 4;
- this.keepAlivePending = false;
- } else if (!this.isSingleplayerOwner()) {
- this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect
+ // Paper start - improve keepalives
+ long now = System.nanoTime();
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive pending = this.player.pendingKeepAlives.peek();
+ if (pending != null && pending.challengeId() == packet.getId()) {
+ this.player.pendingKeepAlives.remove(pending);
+
+ net.minecraft.server.level.ServerPlayer.KeepAliveResponse response = new net.minecraft.server.level.ServerPlayer.KeepAliveResponse(pending.txTimeNS(), now);
+
+ this.player.pingCalculator1m.update(response);
+ this.player.pingCalculator5s.update(response);
+
+ this.latency = this.player.pingCalculator5s.getAvgLatencyMS();
+ return;
+ }
+
+ for (java.util.Iterator<net.minecraft.server.level.ServerPlayer.PendingKeepAlive> itr = this.player.pendingKeepAlives.iterator(); itr.hasNext();) {
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive ka = itr.next();
+ if (ka.challengeId() == packet.getId()) {
+ itr.remove();
+
+ if (!this.processedDisconnect) {
+ LOGGER.info("Disconnecting " + this.player.getScoreboardName() + " for sending keepalive response (" + packet.getId() + ") out-of-order!");
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
+ return;
+ }
+ break;
+ }
}
+
+ if (!this.processedDisconnect) {
+ LOGGER.info("Disconnecting " + this.player.getScoreboardName() + " for sending keepalive response (" + packet.getId() + ") without matching challenge!");
+ this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
+ return;
+ }
+ // Paper end - improve keepalives
}
@Override
@@ -247,20 +275,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
protected void keepConnectionAlive() {
Profiler.get().push("keepAlive");
long millis = Util.getMillis();
- // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings
- // This should effectively place the keepalive handling back to "as it was" before 1.12.2
- final long elapsedTime = millis - this.keepAliveTime;
- if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets
- if (this.keepAlivePending) {
- if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
- }
- // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings
- } else if (this.checkIfClosed(millis)) {
- this.keepAlivePending = true;
- this.keepAliveTime = millis;
- this.keepAliveChallenge = millis;
- this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge));
+ // Paper start - improve keepalives
+ if (this.checkIfClosed(millis) && !this.processedDisconnect) {
+ long currTime = System.nanoTime();
+
+ if ((currTime - this.player.lastKeepAliveTx) >= java.util.concurrent.TimeUnit.SECONDS.toNanos(1L)) {
+ this.player.lastKeepAliveTx = currTime;
+
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive pka = new net.minecraft.server.level.ServerPlayer.PendingKeepAlive(currTime, millis);
+ this.player.pendingKeepAlives.add(pka);
+ this.send(new ClientboundKeepAlivePacket(pka.challengeId()));
+ }
+
+ net.minecraft.server.level.ServerPlayer.PendingKeepAlive oldest = this.player.pendingKeepAlives.peek();
+ if (oldest != null && (currTime - oldest.txTimeNS()) > java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(KEEPALIVE_LIMIT)) {
+ LOGGER.warn(this.player.getScoreboardName() + " was kicked due to keepalive timeout!");
+ this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT);
+ // Paper end - improve keepalives
}
}

View File

@ -0,0 +1,84 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 24 Jun 2025 07:05:51 -0700
Subject: [PATCH] Optimise EntityScheduler ticking
The vast majority of the time, there are no tasks scheduled to
the EntityScheduler. We can avoid iterating the entire entity list
by tracking which schedulers have any tasks scheduled.
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
index 5f2deeb5cc01d8bbeb7449bd4e59c466b3dfdf57..82824ae7ffbced513a8bcace684af94916135e84 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
@@ -96,6 +96,7 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ThrownEnderpearl enderpearl) {
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
}
+ entity.registerScheduler(); // Paper - optimise Folia entity scheduler
}
@Override
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3aee82cc9 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1654,33 +1654,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
+ public final io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList entitySchedulerTickList = new io.papermc.paper.threadedregions.EntityScheduler.EntitySchedulerTickList(); // Paper - optimise Folia entity scheduler
+
protected void tickChildren(BooleanSupplier hasTimeLeft) {
ProfilerFiller profilerFiller = Profiler.get();
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
- // Paper start - Folia scheduler API
- ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
- for (ServerPlayer player : this.playerList.players) {
- if (!this.playerList.players.contains(player)) {
+ // Paper start - optimise Folia entity scheduler
+ ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler)org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
+ for (io.papermc.paper.threadedregions.EntityScheduler scheduler : this.entitySchedulerTickList.getAllSchedulers()) {
+ if (scheduler.isRetired()) {
continue;
}
- final org.bukkit.craftbukkit.entity.CraftEntity bukkit = player.getBukkitEntityRaw();
- if (bukkit != null) {
- bukkit.taskScheduler.executeTick();
- }
+
+ scheduler.executeTick();
}
- getAllLevels().forEach(level -> {
- for (final net.minecraft.world.entity.Entity entity : io.papermc.paper.FeatureHooks.getAllEntities(level)) {
- if (entity.isRemoved() || entity instanceof ServerPlayer) {
- continue;
- }
- final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
- if (bukkit != null) {
- bukkit.taskScheduler.executeTick();
- }
- }
- });
- // Paper end - Folia scheduler API
+ // Paper end - optimise Folia entity scheduler
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
profilerFiller.push("commandFunctions");
this.getFunctions().tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index da880f52920b1101f23ef94f3fd0dbdea218c373..3d2c0a4d3a1f9d3e5cc6cd0cdb988ae1205de821 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -5165,6 +5165,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.getBukkitEntity().taskScheduler.retire();
}
// Paper end - Folia schedulers
+ // Paper start - optimise Folia entity scheduler
+ public final void registerScheduler() {
+ this.getBukkitEntity().taskScheduler.registerTo(net.minecraft.server.MinecraftServer.getServer().entitySchedulerTickList);
+ }
+ // Paper end - optimise Folia entity scheduler
@Override
public void setLevelCallback(EntityInLevelCallback levelCallback) {

View File

@ -1,6 +1,6 @@
--- /dev/null
+++ b/io/papermc/paper/FeatureHooks.java
@@ -1,0 +_,241 @@
@@ -1,0 +_,246 @@
+package io.papermc.paper;
+
+import io.papermc.paper.command.PaperSubcommand;
@ -33,6 +33,11 @@
+
+public final class FeatureHooks {
+
+ // this includes non-accessible entities
+ public static Iterable<Entity> getAllEntities(final net.minecraft.server.level.ServerLevel world) {
+ return ((net.minecraft.world.level.entity.LevelEntityGetterAdapter<Entity>)world.getEntities()).sectionStorage.getAllEntities();
+ }
+
+ public static void setPlayerChunkUnloadDelay(final long ticks) {
+ }
+

View File

@ -1,6 +1,13 @@
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -176,6 +_,11 @@
@@ -170,12 +_,18 @@
@Override
public boolean isRestricted(CommandNode<CommandSourceStack> node) {
+ if (node.getRequirement() instanceof PermissionSource.RestrictedMarker) return true; // Paper - restricted api
return node.getRequirement() instanceof PermissionCheck<?> permissionCheck && permissionCheck.requiredLevel() > 0;
}
};
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
public Commands(Commands.CommandSelection selection, CommandBuildContext context) {
@ -55,76 +62,29 @@
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
}
@@ -289,9 +_,41 @@
return new ParseResults<>(commandContextBuilder, parseResults.getReader(), parseResults.getExceptions());
}
+ // CraftBukkit start
+ public void dispatchServerCommand(CommandSourceStack sender, String command) {
+ com.google.common.base.Joiner joiner = com.google.common.base.Joiner.on(" ");
+ if (command.startsWith("/")) {
+ command = command.substring(1);
+ }
+
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(sender.getBukkitSender(), command);
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+ command = event.getCommand();
+
+ String[] args = command.split(" ");
+ if (args.length == 0) return; // Paper - empty commands shall not be dispatched
+
+ // Paper - Fix permission levels for command blocks
+
+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher
+
+ String newCommand = joiner.join(args);
+ this.performPrefixedCommand(sender, newCommand, newCommand);
+ }
+ // CraftBukkit end
+
public void performPrefixedCommand(CommandSourceStack source, String command) {
+ // CraftBukkit start
+ this.performPrefixedCommand(source, command, command);
+ }
+
+ public void performPrefixedCommand(CommandSourceStack source, String command, String label) {
command = trimOptionalPrefix(command);
- this.performCommand(this.dispatcher.parse(command, source), command);
+ this.performCommand(this.dispatcher.parse(command, source), command, label);
+ // CraftBukkit end
}
public static String trimOptionalPrefix(String command) {
@@ -299,9 +_,20 @@
@@ -299,6 +_,13 @@
}
public void performCommand(ParseResults<CommandSourceStack> parseResults, String command) {
+ // CraftBukkit start
+ this.performCommand(parseResults, command, command);
+ // Paper start
+ this.performCommand(parseResults, command, false);
+ }
+
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, String label) {
+ // CraftBukkit end
+ // Paper start
+ this.performCommand(parseResults, command, label, false);
+ }
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, String label, boolean throwCommandError) {
+ public void performCommand(ParseResults<CommandSourceStack> parseResults, String command, boolean throwCommandError) {
+ org.spigotmc.AsyncCatcher.catchOp("Cannot perform command async");
+ // Paper end
CommandSourceStack commandSourceStack = parseResults.getContext().getSource();
Profiler.get().push(() -> "/" + command);
- ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
+ ContextChain contextChain = this.finishParsing(parseResults, command, commandSourceStack, label); // CraftBukkit // Paper - Add UnknownCommandEvent
try {
if (contextChain != null) {
@@ -313,9 +_,10 @@
ContextChain<CommandSourceStack> contextChain = finishParsing(parseResults, command, commandSourceStack);
@@ -312,10 +_,13 @@
)
);
}
} catch (Exception var12) {
+ if (throwCommandError) throw var12; // Paper
- } catch (Exception var12) {
+ // Paper start
+ } catch (Throwable var12) { // always gracefully handle it, no matter how bad:tm:
+ if (throwCommandError) throw var12; // rethrow directly if requested
+ // Paper end
MutableComponent mutableComponent = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage());
- if (LOGGER.isDebugEnabled()) {
- LOGGER.error("Command exception: /{}", command, var12);
@ -133,12 +93,12 @@
StackTraceElement[] stackTrace = var12.getStackTrace();
for (int i = 0; i < Math.min(stackTrace.length, 3); i++) {
@@ -341,18 +_,22 @@
@@ -341,13 +_,17 @@
}
@Nullable
- private static ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent
+ private ContextChain<CommandSourceStack> finishParsing(ParseResults<CommandSourceStack> parseResults, String command, CommandSourceStack source) {
try {
validateParseResults(parseResults);
return ContextChain.tryFlatten(parseResults.getContext().build(command))
@ -153,12 +113,6 @@
if (var7.getInput() != null && var7.getCursor() >= 0) {
int min = Math.min(var7.getInput().length(), var7.getCursor());
MutableComponent mutableComponent = Component.empty()
.withStyle(ChatFormatting.GRAY)
- .withStyle(style -> style.withClickEvent(new ClickEvent.SuggestCommand("/" + command)));
+ .withStyle(style -> style.withClickEvent(new ClickEvent.SuggestCommand("/" + label))); // CraftBukkit // Paper
if (min > 10) {
mutableComponent.append(CommonComponents.ELLIPSIS);
}
@@ -364,7 +_,17 @@
}

View File

@ -1,16 +1,18 @@
--- a/net/minecraft/commands/PermissionSource.java
+++ b/net/minecraft/commands/PermissionSource.java
@@ -9,9 +_,20 @@
@@ -9,9 +_,22 @@
return this.hasPermission(2);
}
- public record Check<T extends PermissionSource>(@Override int requiredLevel) implements PermissionCheck<T> {
+ public record Check<T extends PermissionSource>(@Override int requiredLevel, java.util.concurrent.atomic.AtomicReference<com.mojang.brigadier.tree.CommandNode<CommandSourceStack>> vanillaNode) implements PermissionCheck<T> { // Paper
+ // Paper start - Vanilla Command permission checking
+ // Paper start - Vanilla Command permission checking & expose restricted API
+ interface RestrictedMarker { }
+
+ public record Check<T extends PermissionSource>(@Override int requiredLevel, java.util.concurrent.atomic.AtomicReference<com.mojang.brigadier.tree.CommandNode<CommandSourceStack>> vanillaNode) implements PermissionCheck<T> {
+ public Check(int requiredLevel) {
+ this(requiredLevel, new java.util.concurrent.atomic.AtomicReference<>());
+ }
+ // Paper end - Vanilla Command permission checking
+ // Paper end - Vanilla Command permission checking & expose restricted API
@Override
public boolean test(T source) {
+ // Paper start - Vanilla Command permission checking

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/network/protocol/game/VecDeltaCodec.java
+++ b/net/minecraft/network/protocol/game/VecDeltaCodec.java
@@ -5,16 +_,16 @@
@@ -5,7 +_,7 @@
public class VecDeltaCodec {
private static final double TRUNCATION_STEPS = 4096.0;
@ -9,14 +9,3 @@
@VisibleForTesting
static long encode(double value) {
- return Math.round(value * 4096.0);
+ return Math.round(value * 4096.0); // Paper - Fix MC-4; diff on change
}
@VisibleForTesting
static double decode(long value) {
- return value / 4096.0;
+ return value / 4096.0; // Paper - Fix MC-4; diff on change
}
public Vec3 decode(long x, long y, long z) {

View File

@ -172,7 +172,7 @@
if (profiledDuration != null) {
profiledDuration.finish(true);
}
@@ -364,25 +_,265 @@
@@ -364,25 +_,266 @@
protected void forceDifficulty() {
}
@ -439,6 +439,7 @@
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins
+ this.server.getCommandMap().registerServerAliases(); // Paper - relocate initial CommandMap#registerServerAliases() call
+ ((org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap()).initializeCommands();
+ this.server.getPluginManager().callEvent(new org.bukkit.event.server.ServerLoadEvent(org.bukkit.event.server.ServerLoadEvent.LoadType.STARTUP));
+ this.connection.acceptConnections();
@ -983,16 +984,25 @@
ObjectArrayList<GameProfile> list = new ObjectArrayList<>(min);
int randomInt = Mth.nextInt(this.random, 0, players.size() - min);
@@ -1040,17 +_,66 @@
@@ -1040,17 +_,75 @@
protected void tickChildren(BooleanSupplier hasTimeLeft) {
ProfilerFiller profilerFiller = Profiler.get();
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
+ this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
+ // Paper start - Folia scheduler API
+ ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
+ for (ServerPlayer player : this.playerList.players) {
+ if (!this.playerList.players.contains(player)) {
+ continue;
+ }
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = player.getBukkitEntityRaw();
+ if (bukkit != null) {
+ bukkit.taskScheduler.executeTick();
+ }
+ }
+ getAllLevels().forEach(level -> {
+ for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) {
+ if (entity.isRemoved()) {
+ for (final net.minecraft.world.entity.Entity entity : io.papermc.paper.FeatureHooks.getAllEntities(level)) {
+ if (entity.isRemoved() || entity instanceof ServerPlayer) {
+ continue;
+ }
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();

View File

@ -273,7 +273,7 @@
}
@Override
@@ -291,13 +_,23 @@
@@ -291,12 +_,20 @@
}
public void handleConsoleInput(String msg, CommandSourceStack source) {
@ -284,23 +284,19 @@
public void handleConsoleInputs() {
- while (!this.consoleInput.isEmpty()) {
- ConsoleInput consoleInput = this.consoleInput.remove(0);
- this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg);
+ // Paper start - Perf: use proper queue
+ ConsoleInput servercommand;
+ while ((servercommand = this.serverCommandQueue.poll()) != null) {
+ ConsoleInput consoleInput;
+ while ((consoleInput = this.serverCommandQueue.poll()) != null) {
+ // Paper end - Perf: use proper queue
+ // CraftBukkit start - ServerCommand for preprocessing
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, servercommand.msg);
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(this.console, consoleInput.msg);
+ this.server.getPluginManager().callEvent(event);
+ if (event.isCancelled()) continue;
+ servercommand = new ConsoleInput(event.getCommand(), servercommand.source);
+
+ // this.getCommands().performPrefixedCommand(servercommand.source, servercommand.msg); // Called in dispatchServerCommand
+ this.server.dispatchServerCommand(this.console, servercommand);
+ consoleInput = new ConsoleInput(event.getCommand(), consoleInput.source);
+ // CraftBukkit end
this.getCommands().performPrefixedCommand(consoleInput.source, consoleInput.msg);
}
}
@@ -430,7 +_,11 @@
@Override
public boolean enforceSecureProfile() {
@ -314,7 +310,7 @@
}
@Override
@@ -515,14 +_,54 @@
@@ -515,14 +_,53 @@
@Override
public String getPluginNames() {
@ -365,8 +361,7 @@
+ if (event.isCancelled()) {
+ return;
+ }
+ ConsoleInput serverCommand = new ConsoleInput(event.getCommand(), wrapper);
+ this.server.dispatchServerCommand(event.getSender(), serverCommand);
+ this.getCommands().performPrefixedCommand(wrapper, event.getCommand());
+ });
+ return rconConsoleSource.getCommandResponse();
+ // CraftBukkit end

View File

@ -1334,7 +1334,8 @@
if (traceItem) {
- ItemStack itemStack = itemEntity != null ? itemEntity.getItem() : ItemStack.EMPTY;
if (!itemStack.isEmpty()) {
this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), droppedItem.getCount());
- this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), droppedItem.getCount());
+ this.awardStat(Stats.ITEM_DROPPED.get(itemStack.getItem()), itemStack.getCount()); // Paper - use size from dropped item
this.awardStat(Stats.DROP);
}
}

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -29,30 +_,67 @@
@@ -29,14 +_,14 @@
import net.minecraft.util.profiling.Profiler;
import org.slf4j.Logger;
@ -15,11 +15,9 @@
- protected final Connection connection;
+ public final Connection connection; // Paper
private final boolean transferred;
- private long keepAliveTime;
+ private long keepAliveTime = Util.getMillis(); // Paper
private long keepAliveTime;
private boolean keepAlivePending;
private long keepAliveChallenge;
private long closedListenerTime;
@@ -45,14 +_,51 @@
private boolean closed = false;
private int latency;
private volatile boolean suspendFlushingOnServerThread = false;

View File

@ -295,7 +295,7 @@
rootVehicle.absSnapTo(d, d1, d2, f, f1);
+ // CraftBukkit start - fire PlayerMoveEvent TODO: this should be removed.
+ this.player.absSnapTo(x, y, z, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff
+ this.player.absSnapTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // Paper - TODO: This breaks alot of stuff
+ org.bukkit.entity.Player player = this.getCraftPlayer();
+ if (!this.hasMoved) {
+ this.lastPosX = prevX;
@ -1146,8 +1146,7 @@
return;
case RELEASE_USE_ITEM:
- this.player.releaseUsingItem();
+ if (this.player.getUseItem() == this.player.getItemInHand(this.player.getUsedItemHand())) this.player.releaseUsingItem(); // Paper - validate use item before processing release
this.player.releaseUsingItem();
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
return;
case START_DESTROY_BLOCK:
@ -2610,7 +2609,7 @@
if (!this.receivedMovementThisTick) {
this.player.setKnownMovement(Vec3.ZERO);
}
@@ -2078,4 +_,17 @@
@@ -2078,4 +_,29 @@
interface EntityInteraction {
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
}
@ -2627,4 +2626,16 @@
+ return event;
+ }
+ // Paper end - Add fail move event
+
+ // Paper start - Implement click callbacks with custom click action
+ @Override
+ public void handleCustomClickAction(final net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket packet) {
+ super.handleCustomClickAction(packet);
+ if (packet.id().equals(io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CLICK_CALLBACK_RESOURCE_LOCATION)) {
+ packet.payload().ifPresent(tag ->
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.tryRunCallback(this.player.getBukkitEntity(), tag)
+ );
+ }
+ }
+ // Paper end - Implement click callbacks with custom click action
}

View File

@ -85,7 +85,7 @@
+ // Paper end - Share random for entities to make them more random
+ public @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
+
+ private @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity;
+ private volatile @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity; // Paper - Folia schedulers - volatile
+
+ public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
+ if (this.bukkitEntity == null) {
@ -1786,7 +1786,7 @@
}
public void addDeltaMovement(Vec3 addend) {
@@ -3661,9 +_,45 @@
@@ -3661,9 +_,35 @@
return this.getZ((2.0 * this.random.nextDouble() - 1.0) * scale);
}
@ -1815,16 +1815,6 @@
+ return;
+ }
+ // Paper end - Block invalid positions and bounding box
+ // Paper start - Fix MC-4
+ if (this instanceof ItemEntity) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
+ // encode/decode from VecDeltaCodec todo computation changed?
+ x = Mth.lfloor(x * 4096.0) * (1 / 4096.0);
+ y = Mth.lfloor(y * 4096.0) * (1 / 4096.0);
+ z = Mth.lfloor(z * 4096.0) * (1 / 4096.0);
+ }
+ }
+ // Paper end - Fix MC-4
if (this.position.x != x || this.position.y != y || this.position.z != z) {
+ synchronized (this.posLock) { // Paper - detailed watchdog information
this.position = new Vec3(x, y, z);

View File

@ -27,3 +27,25 @@
}
}
@@ -120,7 +_,12 @@
return this.level().getBlockState(this.pos).is(BlockTags.FENCES);
}
+ // Paper start - Track if a knot was created
public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos) {
+ return getOrCreateKnot(level, pos, null);
+ }
+ public static LeashFenceKnotEntity getOrCreateKnot(Level level, BlockPos pos, @Nullable org.apache.commons.lang3.mutable.MutableBoolean created) {
+ // Paper end - Track if a knot was created
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
@@ -134,7 +_,7 @@
}
LeashFenceKnotEntity leashFenceKnotEntity1 = new LeashFenceKnotEntity(level, pos);
- level.addFreshEntity(leashFenceKnotEntity1);
+ if (level.addFreshEntity(leashFenceKnotEntity1) && created != null) { created.setTrue(); } // Paper - Track if a knot was created
return leashFenceKnotEntity1;
}

View File

@ -24,6 +24,21 @@
}
public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack itemStack, double deltaX, double deltaY, double deltaZ) {
@@ -79,6 +_,14 @@
this.bobOffs = other.bobOffs;
}
+ // Paper start - Require item entities to send their location precisely (Fixes MC-4)
+ {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.sendFullPosForItemEntities) {
+ this.setRequiresPrecisePosition(true);
+ }
+ }
+ // Paper end - Require item entities to send their location precisely (Fixes MC-4)
+
@Override
public boolean dampensVibrations() {
return this.getItem().is(ItemTags.DAMPENS_VIBRATIONS);
@@ -116,7 +_,7 @@
@Override
public void tick() {

View File

@ -43,23 +43,36 @@
if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
LocalDate localDate = LocalDate.now();
int i = localDate.get(ChronoField.DAY_OF_MONTH);
@@ -196,9 +_,19 @@
@@ -188,7 +_,8 @@
@Override
public void performRangedAttack(LivingEntity target, float distanceFactor) {
- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
+ net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent
+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent
ItemStack projectile = this.getProjectile(itemInHand);
AbstractArrow arrow = this.getArrow(projectile, distanceFactor, itemInHand);
double d = target.getX() - this.getX();
@@ -196,9 +_,21 @@
double d2 = target.getZ() - this.getZ();
double squareRoot = Math.sqrt(d * d + d2 * d2);
if (this.level() instanceof ServerLevel serverLevel) {
+ // CraftBukkit start
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), arrow.getPickupItem(), arrow, net.minecraft.world.InteractionHand.MAIN_HAND, distanceFactor, true); // Paper - improve entity shoot bow event, add arrow stack to event
- Projectile.spawnProjectileUsingShoot(
+ Projectile.Delayed<AbstractArrow> delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed
arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
);
+
+ // Paper start - call EntityShootBowEvent
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, arrow.getPickupItem(), arrow, hand, distanceFactor, true);
+ if (event.isCancelled()) {
+ event.getProjectile().remove();
+ return;
+ }
+
+ if (event.getProjectile() == arrow.getBukkitEntity()) {
+ // CraftBukkit end
Projectile.spawnProjectileUsingShoot(
arrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
);
+ } // CraftBukkit
+ delayedEntity.spawn();
+ }
+ // Paper end - call EntityShootBowEvent
}
this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));

View File

@ -1,22 +1,35 @@
--- a/net/minecraft/world/entity/monster/Illusioner.java
+++ b/net/minecraft/world/entity/monster/Illusioner.java
@@ -180,9 +_,19 @@
@@ -172,7 +_,8 @@
@Override
public void performRangedAttack(LivingEntity target, float distanceFactor) {
- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
+ net.minecraft.world.InteractionHand hand = ProjectileUtil.getWeaponHoldingHand(this, Items.BOW); // Paper - call EntityShootBowEvent
+ ItemStack itemInHand = this.getItemInHand(hand); // Paper - call EntityShootBowEvent
ItemStack projectile = this.getProjectile(itemInHand);
AbstractArrow mobArrow = ProjectileUtil.getMobArrow(this, projectile, distanceFactor, itemInHand);
double d = target.getX() - this.getX();
@@ -180,9 +_,21 @@
double d2 = target.getZ() - this.getZ();
double squareRoot = Math.sqrt(d * d + d2 * d2);
if (this.level() instanceof ServerLevel serverLevel) {
+ // Paper start - EntityShootBowEvent
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), mobArrow.getPickupItem(), mobArrow, target.getUsedItemHand(), distanceFactor, true);
- Projectile.spawnProjectileUsingShoot(
+ Projectile.Delayed<AbstractArrow> delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed
mobArrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
);
+
+ // Paper start - call EntityShootBowEvent
+ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, itemInHand, mobArrow.getPickupItem(), mobArrow, hand, distanceFactor, true);
+ if (event.isCancelled()) {
+ event.getProjectile().remove();
+ return;
+ }
+
+ if (event.getProjectile() == mobArrow.getBukkitEntity()) {
Projectile.spawnProjectileUsingShoot(
mobArrow, serverLevel, projectile, d, d1 + squareRoot * 0.2F, d2, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4
);
+ delayedEntity.spawn();
+ }
+ // Paper end - EntityShootBowEvent
+ // Paper end - call EntityShootBowEvent
}
this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/world/item/LeadItem.java
+++ b/net/minecraft/world/item/LeadItem.java
@@ -26,24 +_,43 @@
@@ -26,24 +_,46 @@
if (blockState.is(BlockTags.FENCES)) {
Player player = context.getPlayer();
if (!level.isClientSide && player != null) {
@ -22,8 +22,11 @@
+ for (java.util.Iterator<Leashable> iterator = list.iterator(); iterator.hasNext();) { // Paper - use iterator to remove
+ Leashable leashable = iterator.next(); // Paper - use iterator to remove
if (leashFenceKnotEntity == null) {
leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos);
- leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos);
+ // CraftBukkit start - fire HangingPlaceEvent
+ org.apache.commons.lang3.mutable.MutableBoolean created = new org.apache.commons.lang3.mutable.MutableBoolean(false);
+ leashFenceKnotEntity = LeashFenceKnotEntity.getOrCreateKnot(level, pos, created);
+ if (created.booleanValue()) {
+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(interactionHand);
+ org.bukkit.event.hanging.HangingPlaceEvent event = new org.bukkit.event.hanging.HangingPlaceEvent((org.bukkit.entity.Hanging) leashFenceKnotEntity.getBukkitEntity(), player != null ? (org.bukkit.entity.Player) player.getBukkitEntity() : null, org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.block.BlockFace.SELF, hand);
+ level.getCraftServer().getPluginManager().callEvent(event);
@ -32,6 +35,7 @@
+ leashFenceKnotEntity.discard(null); // CraftBukkit - add Bukkit remove cause
+ return InteractionResult.PASS;
+ }
+ }
+ // CraftBukkit end
leashFenceKnotEntity.playPlacementSound();
}

View File

@ -12,12 +12,18 @@
public int getSuccessCount() {
return this.successCount;
@@ -108,7 +_,7 @@
@@ -108,7 +_,13 @@
this.successCount++;
}
});
- server.getCommands().performPrefixedCommand(commandSourceStack, this.command);
+ server.getCommands().dispatchServerCommand(commandSourceStack, this.command); // CraftBukkit
+ // Paper start - ServerCommandEvent
+ org.bukkit.event.server.ServerCommandEvent event = new org.bukkit.event.server.ServerCommandEvent(commandSourceStack.getBukkitSender(), net.minecraft.commands.Commands.trimOptionalPrefix(this.command));
+ if (!event.callEvent()) {
+ return true;
+ }
+ server.getCommands().performPrefixedCommand(commandSourceStack, event.getCommand());
+ // Paper end - ServerCommandEvent
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Executing command block");
CrashReportCategory crashReportCategory = crashReport.addCategory("Command to be executed");

View File

@ -30,7 +30,7 @@
- builder.withParameter(LootContextParams.EXPLOSION_RADIUS, explosion.radius());
+ // CraftBukkit start - add yield
+ if (explosion instanceof net.minecraft.world.level.ServerExplosion serverExplosion && serverExplosion.yield < 1.0F) {
+ builder.withParameter(LootContextParams.EXPLOSION_RADIUS, serverExplosion.yield == 0 ? 0 : 1.0F / serverExplosion.yield);
+ builder.withParameter(LootContextParams.EXPLOSION_RADIUS, 1.0F / serverExplosion.yield);
+ // CraftBukkit end
}

View File

@ -0,0 +1,15 @@
--- a/net/minecraft/world/level/entity/EntitySection.java
+++ b/net/minecraft/world/level/entity/EntitySection.java
@@ -19,6 +_,12 @@
this.storage = new ClassInstanceMultiMap<>(entityClazz);
}
+ // Paper start - support retrieving all entities, regardless of whether they are accessible
+ public void getEntities(java.util.List<T> into) {
+ into.addAll(this.storage);
+ }
+ // Paper end - support retrieving all entities, regardless of whether they are accessible
+
public void add(T entity) {
this.storage.add(entity);
}

View File

@ -0,0 +1,19 @@
--- a/net/minecraft/world/level/entity/EntitySectionStorage.java
+++ b/net/minecraft/world/level/entity/EntitySectionStorage.java
@@ -34,6 +_,16 @@
this.intialSectionVisibility = initialSectionVisibility;
}
+ // Paper start - support retrieving all entities, regardless of whether they are accessible
+ public Iterable<T> getAllEntities() {
+ java.util.List<T> ret = new java.util.ArrayList<>();
+ for (EntitySection<T> section : this.sections.values()) {
+ section.getEntities(ret);
+ }
+ return ret;
+ }
+ // Paper end - support retrieving all entities, regardless of whether they are accessible
+
public void forEachAccessibleNonEmptySection(AABB boundingBox, AbortableIterationConsumer<EntitySection<T>> consumer) {
int sectionPosCoord = SectionPos.posToSectionCoord(boundingBox.minX - 2.0);
int sectionPosCoord1 = SectionPos.posToSectionCoord(boundingBox.minY - 4.0);

View File

@ -0,0 +1,11 @@
--- a/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
+++ b/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
@@ -8,7 +_,7 @@
public class LevelEntityGetterAdapter<T extends EntityAccess> implements LevelEntityGetter<T> {
private final EntityLookup<T> visibleEntities;
- private final EntitySectionStorage<T> sectionStorage;
+ public final EntitySectionStorage<T> sectionStorage; // Paper - public
public LevelEntityGetterAdapter(EntityLookup<T> visibleEntities, EntitySectionStorage<T> sectionStorage) {
this.visibleEntities = visibleEntities;

View File

@ -0,0 +1,10 @@
--- a/net/minecraft/world/waypoints/WaypointTransmitter.java
+++ b/net/minecraft/world/waypoints/WaypointTransmitter.java
@@ -20,6 +_,7 @@
Waypoint.Icon waypointIcon();
static boolean doesSourceIgnoreReceiver(LivingEntity entity, ServerPlayer player) {
+ if (!player.getBukkitEntity().canSee(entity.getBukkitEntity())) return true; // Paper - ignore if entity is hidden from player
if (player.isSpectator()) {
return false;
} else if (!entity.isSpectator() && !entity.hasIndirectPassenger(player)) {

View File

@ -1,10 +1,12 @@
package io.papermc.paper;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.world.damagesource.CombatEntry;
import io.papermc.paper.world.damagesource.FallLocationType;
import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper;
import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper;
import net.minecraft.Optionull;
import net.minecraft.commands.PermissionSource;
import net.minecraft.world.damagesource.FallLocation;
import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.block.CraftBiome;
@ -16,6 +18,7 @@ import org.bukkit.damage.DamageSource;
import org.bukkit.entity.LivingEntity;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.function.Predicate;
@NullMarked
public class PaperServerInternalAPIBridge implements InternalAPIBridge {
@ -71,4 +74,16 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge {
damageSource, damage, fallLocation, fallDistance
));
}
@Override
public Predicate<CommandSourceStack> restricted(final Predicate<CommandSourceStack> predicate) {
record RestrictedPredicate(Predicate<CommandSourceStack> predicate) implements Predicate<CommandSourceStack>, PermissionSource.RestrictedMarker {
@Override
public boolean test(final CommandSourceStack commandSourceStack) {
return this.predicate.test(commandSourceStack);
}
}
return new RestrictedPredicate(predicate);
}
}

View File

@ -1,5 +1,6 @@
package io.papermc.paper.adventure;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
@ -14,6 +15,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.EntityNBTComponent;
@ -37,6 +39,9 @@ import net.kyori.adventure.text.format.TextDecoration;
import net.minecraft.commands.arguments.selector.SelectorPattern;
import net.minecraft.core.UUIDUtil;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagParser;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.chat.contents.KeybindContents;
@ -44,6 +49,7 @@ import net.minecraft.network.chat.contents.ScoreContents;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.Item;
@ -63,7 +69,14 @@ import static net.kyori.adventure.text.TranslationArgument.numeric;
@DefaultQualifier(NonNull.class)
public final class AdventureCodecs {
public static final Codec<BinaryTagHolder> BINARY_TAG_HOLDER_CODEC = ExtraCodecs.NBT.flatComapMap(tag -> BinaryTagHolder.encode(tag, PaperAdventure.NBT_CODEC), api -> {
try {
final Tag tag = api.get(PaperAdventure.NBT_CODEC);
return DataResult.success(tag);
} catch (CommandSyntaxException e) {
return DataResult.error(e::getMessage);
}
});
public static final Codec<Component> COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec);
public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_COMPONENT_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(COMPONENT_CODEC);
@ -89,27 +102,37 @@ public final class AdventureCodecs {
return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key");
}, Key::asString);
static final Function<ClickEvent, String> TEXT_PAYLOAD_EXTRACTOR = a -> ((ClickEvent.Payload.Text) a.payload()).value();
/*
* Click
*/
static final MapCodec<ClickEvent> OPEN_URL_CODEC = mapCodec((instance) -> instance.group(
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> URI.create(!a.value().contains("://") ? "https://" + a.value() : a.value()))
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> {
final String url = ((ClickEvent.Payload.Text) a.payload()).value();
return URI.create(!url.contains("://") ? "https://" + url : url);
}
)
).apply(instance, (url) -> ClickEvent.openUrl(url.toString())));
static final MapCodec<ClickEvent> OPEN_FILE_CODEC = mapCodec((instance) -> instance.group(
Codec.STRING.fieldOf("path").forGetter(ClickEvent::value)
Codec.STRING.fieldOf("path").forGetter(TEXT_PAYLOAD_EXTRACTOR)
).apply(instance, ClickEvent::openFile));
static final MapCodec<ClickEvent> RUN_COMMAND_CODEC = mapCodec((instance) -> instance.group(
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
).apply(instance, ClickEvent::runCommand));
static final MapCodec<ClickEvent> SUGGEST_COMMAND_CODEC = mapCodec((instance) -> instance.group(
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
).apply(instance, ClickEvent::suggestCommand));
static final MapCodec<ClickEvent> CHANGE_PAGE_CODEC = mapCodec((instance) -> instance.group(
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> Integer.parseInt(a.value()))
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> ((ClickEvent.Payload.Int) a.payload()).integer())
).apply(instance, ClickEvent::changePage));
static final MapCodec<ClickEvent> COPY_TO_CLIPBOARD_CODEC = mapCodec((instance) -> instance.group(
Codec.STRING.fieldOf("value").forGetter(ClickEvent::value)
Codec.STRING.fieldOf("value").forGetter(TEXT_PAYLOAD_EXTRACTOR)
).apply(instance, ClickEvent::copyToClipboard));
static final MapCodec<ClickEvent> CUSTOM_CODEC = mapCodec((instance) -> instance.group(
KEY_CODEC.fieldOf("id").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).key()),
BINARY_TAG_HOLDER_CODEC.fieldOf("payload").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).nbt())
).apply(instance, ClickEvent::custom));
static final ClickEventType OPEN_URL_CLICK_EVENT_TYPE = new ClickEventType(OPEN_URL_CODEC, "open_url");
static final ClickEventType OPEN_FILE_CLICK_EVENT_TYPE = new ClickEventType(OPEN_FILE_CODEC, "open_file");
@ -117,7 +140,8 @@ public final class AdventureCodecs {
static final ClickEventType SUGGEST_COMMAND_CLICK_EVENT_TYPE = new ClickEventType(SUGGEST_COMMAND_CODEC, "suggest_command");
static final ClickEventType CHANGE_PAGE_CLICK_EVENT_TYPE = new ClickEventType(CHANGE_PAGE_CODEC, "change_page");
static final ClickEventType COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE = new ClickEventType(COPY_TO_CLIPBOARD_CODEC, "copy_to_clipboard");
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE});
static final ClickEventType CUSTOM_CLICK_EVENT_TYPE = new ClickEventType(CUSTOM_CODEC, "custom");
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE});
record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
@Override
@ -126,22 +150,16 @@ public final class AdventureCodecs {
}
}
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE = he -> {
if (he.action() == ClickEvent.Action.OPEN_URL) {
return OPEN_URL_CLICK_EVENT_TYPE;
} else if (he.action() == ClickEvent.Action.OPEN_FILE) {
return OPEN_FILE_CLICK_EVENT_TYPE;
} else if (he.action() == ClickEvent.Action.RUN_COMMAND) {
return RUN_COMMAND_CLICK_EVENT_TYPE;
} else if (he.action() == ClickEvent.Action.SUGGEST_COMMAND) {
return SUGGEST_COMMAND_CLICK_EVENT_TYPE;
} else if (he.action() == ClickEvent.Action.CHANGE_PAGE) {
return CHANGE_PAGE_CLICK_EVENT_TYPE;
} else if (he.action() == ClickEvent.Action.COPY_TO_CLIPBOARD) {
return COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
} else {
throw new IllegalStateException();
}
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
he -> switch (he.action()) {
case OPEN_URL -> OPEN_URL_CLICK_EVENT_TYPE;
case OPEN_FILE -> OPEN_FILE_CLICK_EVENT_TYPE;
case RUN_COMMAND -> RUN_COMMAND_CLICK_EVENT_TYPE;
case SUGGEST_COMMAND -> SUGGEST_COMMAND_CLICK_EVENT_TYPE;
case CHANGE_PAGE -> CHANGE_PAGE_CLICK_EVENT_TYPE;
case COPY_TO_CLIPBOARD -> COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
case SHOW_DIALOG -> throw new UnsupportedOperationException(); // todo: dialog codec with dialog "api"
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
};
static final Codec<ClickEvent> CLICK_EVENT_CODEC = CLICK_EVENT_TYPE_CODEC.dispatch("action", GET_CLICK_EVENT_TYPE, ClickEventType::codec);

View File

@ -35,8 +35,6 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.TranslationRegistry;
import net.kyori.adventure.translation.Translator;
import net.kyori.adventure.util.Codec;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
@ -78,16 +76,16 @@ import static java.util.Objects.requireNonNull;
public final class PaperAdventure {
private static final Pattern LOCALIZATION_PATTERN = Pattern.compile("%(?:(\\d+)\\$)?s");
public static final ComponentFlattener FLATTENER = ComponentFlattener.basic().toBuilder()
.nestingLimit(30) // todo: should this be configurable? a system property or config value?
.complexMapper(TranslatableComponent.class, (translatable, consumer) -> {
if (!Language.getInstance().has(translatable.key())) {
for (final Translator source : GlobalTranslator.translator().sources()) {
if (source instanceof TranslationRegistry registry && registry.contains(translatable.key())) {
final Language language = Language.getInstance();
final @Nullable String fallback = translatable.fallback();
if (!language.has(translatable.key()) && (fallback == null || !language.has(fallback))) {
if (GlobalTranslator.translator().canTranslate(translatable.key(), Locale.US)) {
consumer.accept(GlobalTranslator.render(translatable, Locale.US));
return;
}
}
}
final @Nullable String fallback = translatable.fallback();
final @NotNull String translated = Language.getInstance().getOrDefault(translatable.key(), fallback != null ? fallback : translatable.key());
final Matcher matcher = LOCALIZATION_PATTERN.matcher(translated);
@ -379,6 +377,7 @@ public final class PaperAdventure {
case PLAYER -> SoundSource.PLAYERS;
case AMBIENT -> SoundSource.AMBIENT;
case VOICE -> SoundSource.VOICE;
case UI -> SoundSource.UI;
};
}

View File

@ -1,24 +1,33 @@
package io.papermc.paper.adventure.providers;
import io.papermc.paper.adventure.PaperAdventure;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.event.ClickEvent;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@SuppressWarnings("UnstableApiUsage") // permitted provider
public class ClickCallbackProviderImpl implements ClickCallback.Provider {
private static final Key CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
private static final String ID_KEY = "id";
public static final ResourceLocation CLICK_CALLBACK_RESOURCE_LOCATION = PaperAdventure.asVanilla(CLICK_CALLBACK_KEY);
public static final CallbackManager CALLBACK_MANAGER = new CallbackManager();
@Override
public @NotNull ClickEvent create(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
return ClickEvent.runCommand("/paper:callback " + CALLBACK_MANAGER.addCallback(callback, options));
final CompoundTag tag = new CompoundTag();
tag.putString(ID_KEY, CALLBACK_MANAGER.addCallback(callback, options).toString());
return ClickEvent.custom(CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
}
public static final class CallbackManager {
@ -48,12 +57,21 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
}
}
public void runCallback(final @NotNull Audience audience, final UUID id) {
public void tryRunCallback(final @NotNull Audience audience, final Tag tag) {
tag.asCompound().flatMap(t -> t.getString(ID_KEY)).ifPresent(s -> {
final UUID id;
try {
id = UUID.fromString(s);
} catch (final IllegalArgumentException ignored) {
return;
}
final StoredCallback callback = this.callbacks.get(id);
if (callback != null && callback.valid()) { //TODO Message if expired/invalid?
if (callback != null && callback.valid()) {
callback.takeUse();
callback.callback.accept(audience);
}
});
}
}

View File

@ -1,35 +0,0 @@
package io.papermc.paper.command;
import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import java.util.UUID;
@DefaultQualifier(NonNull.class)
public class CallbackCommand extends Command {
protected CallbackCommand(final String name) {
super(name);
this.description = "ClickEvent callback";
this.usageMessage = "/callback <uuid>";
}
@Override
public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) {
if (args.length != 1) {
return false;
}
final UUID id;
try {
id = UUID.fromString(args[0]);
} catch (final IllegalArgumentException ignored) {
return false;
}
ClickCallbackProviderImpl.CALLBACK_MANAGER.runCallback(sender, id);
return true;
}
}

View File

@ -23,7 +23,6 @@ public final class PaperCommands {
public static void registerCommands(final MinecraftServer server) {
COMMANDS.put("paper", new PaperCommand("paper"));
COMMANDS.put("callback", new CallbackCommand("callback"));
COMMANDS.put("mspt", new MSPTCommand("mspt"));
COMMANDS.forEach((s, command) -> {

View File

@ -20,6 +20,7 @@ import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.bukkit.command.Command;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -96,7 +97,7 @@ public class BukkitBrigForwardingMap extends HashMap<String, Command> {
public Command put(String key, Command value) {
Command old = this.get(key);
this.getDispatcher().getRoot().removeCommand(key); // Override previous command
if (value instanceof PluginVanillaCommandWrapper wrapper && wrapper.getName().equals(key)) {
if (value instanceof VanillaCommandWrapper wrapper && wrapper.getName().equals(key)) {
// Don't break when some plugin tries to remove and add back a plugin command registered with modern API...
this.getDispatcher().getRoot().addChild((CommandNode) wrapper.vanillaCommand);
} else {

View File

@ -1,6 +1,7 @@
package io.papermc.paper.command.subcommands;
import com.google.common.collect.Maps;
import io.papermc.paper.FeatureHooks;
import io.papermc.paper.command.CommandUtil;
import io.papermc.paper.command.PaperSubcommand;
import java.util.Collections;
@ -9,7 +10,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
@ -102,7 +102,7 @@ public final class EntityCommand implements PaperSubcommand {
ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
Map<ResourceLocation, Integer> nonEntityTicking = Maps.newHashMap();
ServerChunkCache chunkProviderServer = world.getChunkSource();
world.getAllEntities().forEach(e -> {
FeatureHooks.getAllEntities(world).forEach(e -> {
ResourceLocation key = EntityType.getKey(e.getType());
MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));

View File

@ -343,7 +343,7 @@ public class GlobalConfiguration extends ConfigurationPart {
}
}
public int maxJoinsPerTick = 5;
public boolean fixEntityPositionDesync = true;
public boolean sendFullPosForItemEntities = false;
public boolean loadPermissionsYmlBeforePlugins = true;
@Constraints.Min(4)
public int regionFileCacheSize = 256;

View File

@ -82,6 +82,7 @@ interface RemovedConfigurations {
path("unsupported-settings", "allow-grindstone-overstacking"),
path("unsupported-settings", "allow-tripwire-disarming-exploits"),
path("commands", "fix-target-selector-tag-completion"),
path("misc", "fix-entity-position-desync")
};
}

View File

@ -1,6 +1,7 @@
package io.papermc.paper.threadedregions;
import ca.spottedleaf.concurrentutil.util.Validate;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.TickThread;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.world.entity.Entity;
@ -9,6 +10,8 @@ import org.bukkit.craftbukkit.entity.CraftEntity;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
/**
@ -43,6 +46,8 @@ public final class EntityScheduler {
private static final long RETIRED_TICK_COUNT = -1L;
private final Object stateLock = new Object();
private final Long2ObjectOpenHashMap<List<ScheduledTask>> oneTimeDelayed = new Long2ObjectOpenHashMap<>();
private EntitySchedulerTickList scheduledList;
private boolean insideScheduledList;
private final ArrayDeque<ScheduledTask> currentlyExecuting = new ArrayDeque<>();
@ -50,6 +55,51 @@ public final class EntityScheduler {
this.entity = Validate.notNull(entity);
}
// must own state lock
private boolean hasTasks() {
return !this.currentlyExecuting.isEmpty() || !this.oneTimeDelayed.isEmpty();
}
public void registerTo(final EntitySchedulerTickList newTickList) {
synchronized (this.stateLock) {
final EntitySchedulerTickList prevList = this.scheduledList;
if (prevList == newTickList) {
return;
}
this.scheduledList = newTickList;
// make sure tasks scheduled before registration can be ticked
if (prevList == null && this.hasTasks()) {
this.insideScheduledList = true;
}
// transfer to new list
if (this.insideScheduledList) {
if (prevList != null) {
prevList.remove(this);
}
if (newTickList != null) {
newTickList.add(this);
} else {
// retired
this.insideScheduledList = false;
}
}
}
}
/**
* Returns whether this scheduler is retired.
*
* <p>
* Note: This should only be invoked on the owning thread for the entity.
* </p>
* @return whether this scheduler is retired.
*/
public boolean isRetired() {
return this.tickCount == RETIRED_TICK_COUNT;
}
/**
* Retires the scheduler, preventing new tasks from being scheduled and invoking the retired callback
* on all currently scheduled tasks.
@ -66,6 +116,7 @@ public final class EntityScheduler {
throw new IllegalStateException("Already retired");
}
this.tickCount = RETIRED_TICK_COUNT;
this.registerTo(null);
}
final Entity thisEntity = this.entity.getHandleRaw();
@ -127,6 +178,11 @@ public final class EntityScheduler {
this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> {
return new ArrayList<>();
}).add(task);
if (!this.insideScheduledList && this.scheduledList != null) {
this.scheduledList.add(this);
this.insideScheduledList = true;
}
}
return true;
@ -147,6 +203,12 @@ public final class EntityScheduler {
throw new IllegalStateException("Ticking retired scheduler");
}
++this.tickCount;
if (this.scheduledList != null && !this.hasTasks()) {
this.scheduledList.remove(this);
this.insideScheduledList = false;
}
if (this.oneTimeDelayed.isEmpty()) {
toRun = null;
} else {
@ -178,4 +240,34 @@ public final class EntityScheduler {
}
}
}
public static final class EntitySchedulerTickList {
private static final EntityScheduler[] ENTITY_SCHEDULER_ARRAY = new EntityScheduler[0];
private final ReferenceList<EntityScheduler> entitySchedulers = new ReferenceList<>(ENTITY_SCHEDULER_ARRAY);
public boolean add(final EntityScheduler scheduler) {
synchronized (this) {
return this.entitySchedulers.add(scheduler);
}
}
public void remove(final EntityScheduler scheduler) {
synchronized (this) {
this.entitySchedulers.remove(scheduler);
}
}
public EntityScheduler[] getAllSchedulers() {
EntityScheduler[] ret = new EntityScheduler[this.entitySchedulers.size()];
synchronized (this) {
if (ret.length != this.entitySchedulers.size()) {
ret = new EntityScheduler[this.entitySchedulers.size()];
}
System.arraycopy(this.entitySchedulers.getRawDataUnchecked(), 0, ret, 0, this.entitySchedulers.size());
return ret;
}
}
}
}

View File

@ -302,7 +302,6 @@ public final class CraftServer implements Server {
public CraftDataPackManager dataPackManager;
private final CraftServerTickManager serverTickManager;
private final CraftServerLinks serverLinks;
public boolean playerCommandState;
private boolean printSaveWarning;
private CraftIconCache icon;
private boolean overrideAllCommandBlockCommands = false;
@ -613,7 +612,6 @@ public final class CraftServer implements Server {
// Spigot start - Allow vanilla commands to be forced to be the main command
this.commandMap.setFallbackCommands();
// Spigot end
this.commandMap.registerServerAliases();
DefaultPermissions.registerCorePermissions();
CraftDefaultPermissions.registerCorePermissions();
if (!io.papermc.paper.configuration.GlobalConfiguration.get().misc.loadPermissionsYmlBeforePlugins) this.loadCustomPermissions(); // Paper
@ -974,41 +972,13 @@ public final class CraftServer implements Server {
return this.playerList;
}
// NOTE: Should only be called from DedicatedServer.ah()
public boolean dispatchServerCommand(CommandSender sender, ConsoleInput serverCommand) {
if (sender instanceof Conversable) {
Conversable conversable = (Conversable) sender;
if (conversable.isConversing()) {
conversable.acceptConversationInput(serverCommand.msg);
return true;
}
}
try {
this.playerCommandState = true;
return this.dispatchCommand(sender, serverCommand.msg);
} catch (Exception ex) {
this.getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.msg + '"', ex);
return false;
} finally {
this.playerCommandState = false;
}
}
@Override
public boolean dispatchCommand(CommandSender sender, String commandLine) {
Preconditions.checkArgument(sender != null, "sender cannot be null");
public boolean dispatchCommand(CommandSender rawSender, String commandLine) {
Preconditions.checkArgument(rawSender != null, "sender cannot be null");
Preconditions.checkArgument(commandLine != null, "commandLine cannot be null");
org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + commandLine); // Spigot // Paper - Include command in error message
CommandSourceStack sourceStack = VanillaCommandWrapper.getListener(rawSender);
if (this.commandMap.dispatch(sender, commandLine)) {
return true;
}
return this.dispatchCommand(VanillaCommandWrapper.getListener(sender), commandLine);
}
public boolean dispatchCommand(CommandSourceStack sourceStack, String commandLine) {
net.minecraft.commands.Commands commands = this.getHandle().getServer().getCommands();
com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = commands.getDispatcher();
com.mojang.brigadier.ParseResults<CommandSourceStack> results = dispatcher.parse(commandLine, sourceStack);
@ -1018,7 +988,12 @@ public final class CraftServer implements Server {
Command target = this.commandMap.getCommand(args[0].toLowerCase(java.util.Locale.ENGLISH));
try {
commands.performCommand(results, commandLine, commandLine, true);
if (results.getContext().getNodes().isEmpty()) {
return false;
}
Commands.validateParseResults(results);
commands.performCommand(results, commandLine, true);
return true;
} catch (CommandException ex) {
new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args)).callEvent(); // Paper
throw ex;
@ -1027,9 +1002,6 @@ public final class CraftServer implements Server {
new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args)).callEvent(); // Paper
throw new CommandException(msg, ex);
}
// Paper end
return false;
}
@Override
@ -2609,7 +2581,7 @@ public final class CraftServer implements Server {
}
public void checkSaveState() {
if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) {
if (this.printSaveWarning || this.console.autosavePeriod <= 0) {
return;
}
this.printSaveWarning = true;

View File

@ -29,6 +29,7 @@ import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.kyori.adventure.pointer.PointersSupplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
@ -157,6 +158,10 @@ import org.jetbrains.annotations.Nullable;
public class CraftWorld extends CraftRegionAccessor implements World {
public static final int CUSTOM_DIMENSION_OFFSET = 10;
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
private static final PointersSupplier<World> POINTERS_SUPPLIER = PointersSupplier.<World>builder()
.resolving(net.kyori.adventure.identity.Identity.NAME, World::getName)
.resolving(net.kyori.adventure.identity.Identity.UUID, World::getUID)
.build();
private final ServerLevel world;
private WorldBorder worldBorder;
@ -168,7 +173,6 @@ public class CraftWorld extends CraftRegionAccessor implements World {
private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this);
private final Object2IntOpenHashMap<SpawnCategory> spawnCategoryLimit = new Object2IntOpenHashMap<>();
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftWorld.DATA_TYPE_REGISTRY);
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
// Paper start - void damage configuration
private boolean voidDamageEnabled;
private float voidDamageAmount;
@ -2481,14 +2485,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Paper start - implement pointers
@Override
public net.kyori.adventure.pointer.Pointers pointers() {
if (this.adventure$pointers == null) {
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
.withDynamic(net.kyori.adventure.identity.Identity.NAME, this::getName)
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUID)
.build();
}
return this.adventure$pointers;
return POINTERS_SUPPLIER.view(this);
}
// Paper end
}

View File

@ -74,7 +74,7 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
this.loadData(state.getSnapshotNBT());
}
private RegistryAccess getRegistryAccess() {
public RegistryAccess getRegistryAccess() {
LevelAccessor worldHandle = this.getWorldHandle();
return (worldHandle != null) ? worldHandle.registryAccess() : CraftRegistry.getMinecraftRegistry();
}

View File

@ -8,7 +8,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.InclusiveRange;
import net.minecraft.util.ProblemReporter;
@ -51,7 +50,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<SpawnerBlockEnti
}
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(() -> "spawner@" + getLocation(), LOGGER)) {
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getInternalWorld().registryAccess(), spawnData.entityToSpawn());
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getRegistryAccess(), spawnData.entityToSpawn());
Optional<net.minecraft.world.entity.EntityType<?>> type = net.minecraft.world.entity.EntityType.by(valueInput);
return type.map(CraftEntityType::minecraftToBukkit).orElse(null);
}
@ -183,7 +182,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<SpawnerBlockEnti
return null;
}
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(() -> "spawner@" + getLocation(), LOGGER)) {
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getInternalWorld().registryAccess(), spawnData.getEntityToSpawn());
ValueInput valueInput = TagValueInput.create(scopedCollector, this.getRegistryAccess(), spawnData.getEntityToSpawn());
Optional<net.minecraft.world.entity.EntityType<?>> type = net.minecraft.world.entity.EntityType.by(valueInput);
return type.map(CraftEntityType::minecraftToBukkit).map(CraftEntityType::bukkitToString).orElse(null);

View File

@ -2,6 +2,7 @@ package org.bukkit.craftbukkit.command;
import java.util.Set;
import java.util.UUID;
import net.kyori.adventure.pointer.PointersSupplier;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
@ -12,8 +13,12 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
public abstract class ServerCommandSender implements CommandSender {
private static final PointersSupplier<ServerCommandSender> POINTERS_SUPPLIER = PointersSupplier.<ServerCommandSender>builder()
.resolving(net.kyori.adventure.identity.Identity.DISPLAY_NAME, ServerCommandSender::name)
.resolving(net.kyori.adventure.permission.PermissionChecker.POINTER, serverCommandSender -> serverCommandSender::permissionValue)
.build();
public final PermissibleBase perm;
private net.kyori.adventure.pointer.Pointers adventure$pointers;
protected ServerCommandSender() {
this.perm = new PermissibleBase(this);
@ -126,13 +131,6 @@ public abstract class ServerCommandSender implements CommandSender {
@Override
public net.kyori.adventure.pointer.Pointers pointers() {
if (this.adventure$pointers == null) {
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::name)
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
.build();
}
return this.adventure$pointers;
return POINTERS_SUPPLIER.view(this);
}
}

View File

@ -50,7 +50,7 @@ public class VanillaCommandWrapper extends BukkitCommand { // Paper
if (!this.testPermission(sender)) return true;
CommandSourceStack source = VanillaCommandWrapper.getListener(sender);
this.commands().performPrefixedCommand(source, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); // Paper
this.commands().performPrefixedCommand(source, this.toDispatcher(args, this.getName()));
return true;
}

View File

@ -14,6 +14,7 @@ import java.util.Set;
import java.util.UUID;
import io.papermc.paper.entity.LookAnchor;
import java.util.concurrent.CompletableFuture;
import net.kyori.adventure.pointer.PointersSupplier;
import net.kyori.adventure.util.TriState;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
@ -85,13 +86,17 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
private static PermissibleBase perm;
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
static final PointersSupplier<org.bukkit.entity.Entity> POINTERS_SUPPLIER = PointersSupplier.<org.bukkit.entity.Entity>builder()
.resolving(net.kyori.adventure.identity.Identity.DISPLAY_NAME, org.bukkit.entity.Entity::name)
.resolving(net.kyori.adventure.identity.Identity.UUID, org.bukkit.entity.Entity::getUniqueId)
.resolving(net.kyori.adventure.permission.PermissionChecker.POINTER, entity1 -> entity1::permissionValue)
.build();
protected final CraftServer server;
protected Entity entity;
private final EntityType entityType;
private EntityDamageEvent lastDamageEvent;
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftEntity.DATA_TYPE_REGISTRY);
protected net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
// Paper start - Folia shedulers
public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this);
private final io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler apiScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler(this);
@ -670,15 +675,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public net.kyori.adventure.pointer.Pointers pointers() {
if (this.adventure$pointers == null) {
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::name)
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUniqueId)
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
.build();
}
return this.adventure$pointers;
return POINTERS_SUPPLIER.view(this);
}
@Override

View File

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import net.minecraft.core.RegistryAccess;
import net.minecraft.util.ProblemReporter;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.WeightedList;
@ -186,6 +187,11 @@ public class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMin
return this.getHandle().level();
}
@Override
public RegistryAccess getRegistryAccess() {
return this.getHandle().registryAccess();
}
@Override
public net.minecraft.core.BlockPos getInternalPosition() {
return this.getHandle().blockPosition();

View File

@ -44,6 +44,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.PointersSupplier;
import net.kyori.adventure.util.TriState;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.advancements.AdvancementProgress;
@ -213,6 +215,12 @@ import org.jspecify.annotations.Nullable;
@DelegateDeserialization(CraftOfflinePlayer.class)
public class CraftPlayer extends CraftHumanEntity implements Player {
private static final PointersSupplier<Player> POINTERS_SUPPLIER = PointersSupplier.<Player>builder()
.parent(CraftEntity.POINTERS_SUPPLIER)
.resolving(Identity.NAME, Player::getName)
.resolving(Identity.DISPLAY_NAME, Player::displayName)
.resolving(Identity.LOCALE, Player::locale)
.build();
private long firstPlayed = 0;
private long lastPlayed = 0;
@ -3283,17 +3291,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public net.kyori.adventure.pointer.Pointers pointers() {
if (this.adventure$pointers == null) {
this.adventure$pointers = net.kyori.adventure.pointer.Pointers.builder()
.withDynamic(net.kyori.adventure.identity.Identity.DISPLAY_NAME, this::displayName)
.withDynamic(net.kyori.adventure.identity.Identity.NAME, this::getName)
.withDynamic(net.kyori.adventure.identity.Identity.UUID, this::getUniqueId)
.withStatic(net.kyori.adventure.permission.PermissionChecker.POINTER, this::permissionValue)
.withDynamic(net.kyori.adventure.identity.Identity.LOCALE, this::locale)
.build();
}
return this.adventure$pointers;
return POINTERS_SUPPLIER.view(this);
}
@Override

View File

@ -185,11 +185,6 @@ public class CraftItemType<M extends ItemMeta> extends HolderableBase<Item> impl
return expectedItem.isEmpty() ? null : CraftItemType.minecraftToBukkitNew(expectedItem.getItem());
}
// @Override
// public EquipmentSlot getEquipmentSlot() {
// return CraftEquipmentSlot.getSlot(EntityInsentient.getEquipmentSlotForItem(CraftItemStack.asNMSCopy(ItemStack.of(this))));
// }
@Override
public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers() {
return this.getDefaultAttributeModifiers(sg -> true);

View File

@ -34,6 +34,8 @@ public interface PaperSharedSpawnerLogic extends Spawner {
Level getInternalWorld();
RegistryAccess getRegistryAccess();
BlockPos getInternalPosition();
default boolean isActivated() {
return this.getSpawner().isNearPlayer(this.getInternalWorld(), this.getInternalPosition());

View File

@ -11,11 +11,14 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Stream;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
@ -106,23 +109,35 @@ class AdventureCodecsTest {
}
@ParameterizedTest(name = PARAMETERIZED_NAME)
@EnumSource(value = ClickEvent.Action.class, mode = EnumSource.Mode.EXCLUDE, names = {"OPEN_FILE"})
@EnumSource(value = ClickEvent.Action.class, mode = EnumSource.Mode.EXCLUDE, names = {"OPEN_FILE", "SHOW_DIALOG", "CUSTOM"})
void testClickEvent(final ClickEvent.Action action) {
final ClickEvent event = ClickEvent.clickEvent(action, action.name().equals("OPEN_URL") ? "https://google.com" : "1337");
final Tag result = CLICK_EVENT_CODEC.encodeStart(NbtOps.INSTANCE, event).result().orElseThrow();
final ClickEvent event = switch (action) {
case OPEN_URL -> openUrl("https://google.com");
case RUN_COMMAND -> ClickEvent.runCommand("/say hello");
case SUGGEST_COMMAND -> suggestCommand("/suggest hello");
case CHANGE_PAGE -> ClickEvent.changePage(2);
case COPY_TO_CLIPBOARD -> ClickEvent.copyToClipboard("clipboard content");
case CUSTOM -> ClickEvent.custom(key("test"), BinaryTagHolder.binaryTagHolder("3"));
case SHOW_DIALOG, OPEN_FILE -> throw new IllegalArgumentException();
};
final Tag result = CLICK_EVENT_CODEC.encodeStart(NbtOps.INSTANCE, event).result().orElseThrow(() -> new RuntimeException("Failed to encode ClickEvent: " + event));
final net.minecraft.network.chat.ClickEvent nms = net.minecraft.network.chat.ClickEvent.CODEC.decode(NbtOps.INSTANCE, result).result().orElseThrow().getFirst();
assertEquals(event.action().toString(), nms.action().getSerializedName());
switch (nms) {
case net.minecraft.network.chat.ClickEvent.OpenUrl(java.net.URI uri) ->
assertEquals(event.value(), uri.toString());
case net.minecraft.network.chat.ClickEvent.OpenUrl(URI uri) ->
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), uri.toString());
case net.minecraft.network.chat.ClickEvent.SuggestCommand(String command) ->
assertEquals(event.value(), command);
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), command);
case net.minecraft.network.chat.ClickEvent.RunCommand(String command) ->
assertEquals(event.value(), command);
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), command);
case net.minecraft.network.chat.ClickEvent.CopyToClipboard(String value) ->
assertEquals(event.value(), value);
assertEquals(((ClickEvent.Payload.Text) event.payload()).value(), value);
case net.minecraft.network.chat.ClickEvent.ChangePage(int page) ->
assertEquals(event.value(), String.valueOf(page));
assertEquals(((ClickEvent.Payload.Int) event.payload()).integer(), page);
case net.minecraft.network.chat.ClickEvent.Custom(ResourceLocation id, Optional<Tag> payload) -> {
assertEquals(((ClickEvent.Payload.Custom) event.payload()).key().toString(), id.toString());
assertEquals(((ClickEvent.Payload.Custom) event.payload()).nbt(), payload.orElseThrow().asString());
}
default -> throw new AssertionError("Unexpected ClickEvent type: " + nms.getClass());
}
}
@ -294,10 +309,10 @@ class AdventureCodecsTest {
.clickEvent(openUrl("https://github.com"))
.build(),
style()
.hoverEvent(HoverEvent.showEntity(HoverEvent.ShowEntity.showEntity(
Key.key(Key.MINECRAFT_NAMESPACE, "pig"),
.hoverEvent(showEntity(HoverEvent.ShowEntity.showEntity(
key(Key.MINECRAFT_NAMESPACE, "pig"),
UUID.randomUUID(),
Component.text("Dolores", TextColor.color(0x0a1ab9))
text("Dolores", color(0x0a1ab9))
)))
.build()
);