mirror of
https://github.com/PaperMC/Paper.git
synced 2026-11-04 17:00:09 -05:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17fbe152a6 | ||
|
|
8ac3459533 | ||
|
|
b72728d717 | ||
|
|
df17d86a37 |
No files matched your search
@@ -14,18 +14,11 @@
|
||||
<property name="messageKey" value="annotation\.same\.line"/>
|
||||
<property name="arguments" value="^(?!(${type_use_annotations}))"/>
|
||||
</module>
|
||||
<module name="io.papermc.checkstyle.filters.BetterSuppressionSingleFilter">
|
||||
<!--Allows @deprecated on packages. Not in the javadoc spec (javadoc warns), but it is still rendered in the deprecation block and list-->
|
||||
<property name="checks" value="InappropriateJavadocBlockTagsOnPackage"/>
|
||||
<property name="messageKey" value="javadoc\.inappropriate\.tag"/>
|
||||
<property name="arguments" value="^deprecated$"/>
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
|
||||
<!--Javadoc Comments-->
|
||||
<module name="JavadocPackage"/>
|
||||
<!--Misc-->
|
||||
<module name="LineEnding"/>
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
<!--Whitespace-->
|
||||
<module name="FileTabCharacter"/>
|
||||
@@ -45,7 +38,6 @@
|
||||
</module>
|
||||
<module name="AnnotationUseStyle"/>
|
||||
<module name="MissingDeprecated"/>
|
||||
<module name="MissingOverrideOnRecordAccessor"/>
|
||||
|
||||
<!--Block Checks-->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
@@ -63,9 +55,7 @@
|
||||
<!--Coding-->
|
||||
<module name="AvoidDoubleBraceInitialization"/>
|
||||
<module name="AvoidNoArgumentSuperConstructorCall"/>
|
||||
<module name="ConstructorsDeclarationGrouping">
|
||||
<property name="orderByIncreasingParameterCount" value="true"/>
|
||||
</module>
|
||||
<module name="ConstructorsDeclarationGrouping"/>
|
||||
<module name="CovariantEquals"/>
|
||||
<module name="DeclarationOrder">
|
||||
<property name="ignoreConstructors" value="true"/>
|
||||
@@ -73,7 +63,6 @@
|
||||
<module name="DefaultComesLast"/>
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="ExpressionOverBlockLambda"/>
|
||||
<module name="FallThrough"/>
|
||||
<!-- Use non-final variables as an identifier of old code for now -->
|
||||
<!--<module name="FinalLocalVariable">
|
||||
@@ -97,9 +86,6 @@
|
||||
<module name="UnnecessarySemicolonInTryWithResources"/>
|
||||
<module name="UnusedCatchParameterShouldBeUnnamed"/>
|
||||
<module name="UnusedLocalVariable"/>
|
||||
<module name="UnusedPrivateField"/>
|
||||
<module name="UnusedTryResourceShouldBeUnnamed"/>
|
||||
<module name="UseEnhancedSwitch"/>
|
||||
<module name="WhenShouldBeUsed"/>
|
||||
|
||||
<!--Headers--> <!--N/A-->
|
||||
@@ -128,13 +114,9 @@
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @see, @deprecated, @hidden"/>
|
||||
</module>
|
||||
<module name="InappropriateJavadocBlockTagsOnField"/>
|
||||
<module name="InappropriateJavadocBlockTagsOnPackage"/>
|
||||
<module name="InappropriateJavadocBlockTagsOnType"/>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocBlockTagLocation"/>
|
||||
<module name="JavadocContentLocation"/>
|
||||
<module name="JavadocEndCommentDelimiter"/>
|
||||
<module name="JavadocLeadingAsteriskAlign"/>
|
||||
<module name="JavadocMethod">
|
||||
<!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
@@ -142,11 +124,11 @@
|
||||
</module>
|
||||
<module name="JavadocMissingLeadingAsterisk"/>
|
||||
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
|
||||
<module name="JavadocStyle"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="JavadocType"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||
<module name="SummaryJavadoc"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
|
||||
|
||||
<!--Metrics--> <!--N/A-->
|
||||
|
||||
@@ -154,14 +136,12 @@
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="AvoidEscapedUnicodeCharacters"/>
|
||||
<module name="CommentsIndentation"/>
|
||||
<module name="HexLiteralCase"/>
|
||||
<!-- Overlaps with the configured FinalLocalVariable, but is more strict -->
|
||||
<!--<module name="FinalParameters">
|
||||
<property name="tokens" value="METHOD_DEF,CTOR_DEF,LITERAL_CATCH,FOR_EACH_CLAUSE,PATTERN_VARIABLE_DEF"/>
|
||||
</module>-->
|
||||
|
||||
<module name="NoCodeInFile"/>
|
||||
<module name="NumericalPrefixesInfixesSuffixesCharacterCase"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="UpperEll"/>
|
||||
|
||||
@@ -200,7 +180,6 @@
|
||||
<!--Size Violations--> <!--N/A-->
|
||||
|
||||
<!--Whitespace-->
|
||||
<module name="ArrayBracketNoWhitespace"/>
|
||||
<module name="EmptyForInitializerPad"/>
|
||||
<module name="EmptyForIteratorPad"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
@@ -218,11 +197,9 @@
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
<module name="WhitespaceBeforeEmptyBody"/>
|
||||
|
||||
<!--Custom-->
|
||||
<module name="JavadocAlignParameterDescription"/>
|
||||
<module name="JavadocParamTagOrder"/>
|
||||
<module name="NullabilityAnnotations"/>
|
||||
<module name="RedundantNullability"/>
|
||||
<module name="UnnecessaryFullyQualifiedImport"/>
|
||||
|
||||
@@ -477,10 +477,8 @@ public net.minecraft.world.inventory.AbstractMountInventoryMenu mount
|
||||
public net.minecraft.world.inventory.AnvilMenu cost
|
||||
public net.minecraft.world.inventory.AnvilMenu itemName
|
||||
public net.minecraft.world.inventory.AnvilMenu repairItemCountCost
|
||||
public net.minecraft.world.inventory.BrewingStandMenu DATA_COUNT
|
||||
public net.minecraft.world.inventory.BrewingStandMenu brewingStandData
|
||||
public net.minecraft.world.inventory.CraftingMenu access
|
||||
public net.minecraft.world.inventory.LecternMenu DATA_COUNT
|
||||
public net.minecraft.world.inventory.MerchantContainer selectionHint
|
||||
public net.minecraft.world.item.AdventureModePredicate predicates
|
||||
public net.minecraft.world.item.CrossbowItem FIREWORK_POWER
|
||||
@@ -585,7 +583,6 @@ public net.minecraft.world.level.block.entity.LecternBlockEntity bookAccess
|
||||
public net.minecraft.world.level.block.entity.LecternBlockEntity setPage(I)V
|
||||
public net.minecraft.world.level.block.entity.SculkCatalystBlockEntity$CatalystListener bloom(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/util/RandomSource;)V
|
||||
public net.minecraft.world.level.block.entity.SculkShriekerBlockEntity warningLevel
|
||||
public net.minecraft.world.level.block.entity.SignBlockEntity allowOpFeatures
|
||||
public net.minecraft.world.level.block.entity.SkullBlockEntity customName
|
||||
public net.minecraft.world.level.block.entity.SkullBlockEntity noteBlockSound
|
||||
public net.minecraft.world.level.block.entity.SkullBlockEntity owner
|
||||
|
||||
@@ -234,8 +234,7 @@ public class MaterialTags {
|
||||
* Covers all music disc items.
|
||||
*/
|
||||
public static final MaterialSetTag MUSIC_DISCS = new MaterialSetTag(keyFor("music_discs"))
|
||||
.startsWith("MUSIC_DISC_")
|
||||
.ensureSize("MUSIC_DISCS", 22).lock();
|
||||
.startsWith("MUSIC_DISC_").lock();
|
||||
|
||||
/**
|
||||
* Covers all ores.
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import net.kyori.adventure.pointer.Pointered;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.bukkit.GameRules;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -87,123 +81,4 @@ public interface CommandSourceStack {
|
||||
* @see com.mojang.brigadier.builder.ArgumentBuilder#fork(CommandNode, RedirectModifier)
|
||||
*/
|
||||
CommandSourceStack withExecutor(Entity executor);
|
||||
|
||||
/**
|
||||
* Sends a system message to the {@link #getExecutor()} if it is a {@link Player},
|
||||
* otherwise sends a system message to the {@link #getSender()}.
|
||||
*
|
||||
* @param message the message to send
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
void sendReply(ComponentLike message);
|
||||
|
||||
/**
|
||||
* Sends a system message with the MiniMessage format to the {@link #getExecutor()}
|
||||
* if it is a {@link Player}, otherwise sends it to the {@link #getSender()}.
|
||||
*
|
||||
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
|
||||
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
|
||||
* for more information on the format.</p>
|
||||
*
|
||||
* <p>This method provides the target of the system message as a {@link net.kyori.adventure.pointer.Pointered}
|
||||
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
|
||||
*
|
||||
* @param message the MiniMessage message to send
|
||||
* @param resolvers resolvers to use
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
default void sendRichReply(final String message, final TagResolver... resolvers) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.sendReply(MiniMessage.miniMessage().deserialize(
|
||||
message,
|
||||
this.getExecutor() instanceof final Player player ? player : this.getSender(),
|
||||
resolvers
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution
|
||||
* according to vanilla semantics.
|
||||
*
|
||||
* <p>This currently includes checking for environments with suppressed output,
|
||||
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
|
||||
*
|
||||
* @param message the message to send
|
||||
* @param allowInformingAdmins whether admins and console may be informed of this success
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
void sendSuccess(ComponentLike message, boolean allowInformingAdmins);
|
||||
|
||||
/**
|
||||
* Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution
|
||||
* according to vanilla semantics. This method informs admins and the console of this success.
|
||||
*
|
||||
* <p>This currently includes checking for environments with suppressed output,
|
||||
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
|
||||
*
|
||||
* @param message the message to send
|
||||
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
default void sendSuccess(final ComponentLike message) {
|
||||
this.sendSuccess(message, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system message with the MiniMessage format to the {@link #getSender()}, admins, and console indicating
|
||||
* successful command execution according to vanilla semantics. This method informs admins and the console of
|
||||
* this success.
|
||||
*
|
||||
* <p>This currently includes checking for environments with suppressed output,
|
||||
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
|
||||
*
|
||||
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
|
||||
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
|
||||
* for more information on the format.</p>
|
||||
*
|
||||
* <p>This method provides the {@link CommandSender} of this command as a {@link net.kyori.adventure.pointer.Pointered}
|
||||
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
|
||||
*
|
||||
* @param message the MiniMessage message to send
|
||||
* @param resolvers resolvers to use
|
||||
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
default void sendRichSuccess(final String message, final TagResolver... resolvers) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.sendSuccess(MiniMessage.miniMessage().deserialize(message, this.getSender(), resolvers), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system message indicating a failed command execution to the {@link #getSender()}.
|
||||
* Does not apply red styling to the message as vanilla does to allow for custom failure message styling.
|
||||
*
|
||||
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
|
||||
*
|
||||
* @param message the message to send
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
void sendFailure(ComponentLike message);
|
||||
|
||||
/**
|
||||
* Sends a system message with the MiniMessage format indicating a failed command execution to the{@link #getSender()}.
|
||||
* Does not apply red styling to the message as vanilla does to allow for custom failure message styling.
|
||||
*
|
||||
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
|
||||
*
|
||||
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
|
||||
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
|
||||
* for more information on the format.</p>
|
||||
*
|
||||
* <p>This method provides the {@link CommandSender} of this command as a {@link net.kyori.adventure.pointer.Pointered}
|
||||
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
|
||||
*
|
||||
* @param message the MiniMessage message to send
|
||||
* @param resolvers resolvers to use
|
||||
* @throws IllegalArgumentException if the message is null
|
||||
*/
|
||||
default void sendRichFailure(final String message, final TagResolver... resolvers) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.sendFailure(MiniMessage.miniMessage().deserialize(message, this.getSender(), resolvers));
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package io.papermc.paper.event.entity;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.entity.EntityEvent;
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Called when an {@link Entity} lands on the block after falling.
|
||||
* This event is called before {@link org.bukkit.event.entity.EntityDamageEvent}, {@link org.bukkit.event.entity.EntityChangeBlockEvent}, {@link org.bukkit.event.player.PlayerInteractEvent}
|
||||
* and {@link org.bukkit.event.entity.EntityInteractEvent}.
|
||||
*/
|
||||
@NullMarked
|
||||
public class EntityLandEvent extends EntityEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final Block block;
|
||||
private boolean cancelled;
|
||||
private double fallDistance;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public EntityLandEvent(final Entity entity, final Block block, final double fallDistance) {
|
||||
super(entity);
|
||||
this.block = block;
|
||||
this.fallDistance = fallDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the block that the entity landed on}
|
||||
*/
|
||||
public Block getBlock() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to cancel the landing, cancelling this event will prevent
|
||||
* the entity from taking fall damage and interact with the block. (ex: break turtle egg)
|
||||
*
|
||||
* @param cancel true if the event should be cancelled, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(final boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fall distance of the entity before landing.
|
||||
*
|
||||
* @return the fall distance
|
||||
*/
|
||||
public double getFallDistance() {
|
||||
return this.fallDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fall distance of the entity before landing.
|
||||
* Fall distance in the event is used to calculate fall damage and
|
||||
* the chance of transforming {@link org.bukkit.Material#FARMLAND} to {@link org.bukkit.Material#DIRT} and
|
||||
* the type of falling sound when the entity lands on {@link org.bukkit.Material#POWDER_SNOW}.
|
||||
*
|
||||
* @param fallDistance the fall distance
|
||||
* @throws IllegalArgumentException if the fall distance is negative or not finite
|
||||
*/
|
||||
public void setFallDistance(final @NonNegative double fallDistance) {
|
||||
Preconditions.checkArgument(
|
||||
Double.isFinite(fallDistance) && fallDistance >= 0.0,
|
||||
"Fall distance must be finite and non-negative"
|
||||
);
|
||||
this.fallDistance = fallDistance;
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package io.papermc.paper.event.entity;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.entity.EntityEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Called when an entity is launched upward by a Potent Sulfur geyser.
|
||||
*/
|
||||
@NullMarked
|
||||
public class EntityPotentSulfurLaunchEvent extends EntityEvent {
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
|
||||
private final Block sulfurBlock;
|
||||
private final Location sourceLocation;
|
||||
|
||||
private final Vector launchVelocity;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public EntityPotentSulfurLaunchEvent(
|
||||
final Entity entity,
|
||||
final Block sulfurBlock,
|
||||
final Location sourceLocation,
|
||||
final Vector launchVelocity
|
||||
) {
|
||||
super(entity);
|
||||
this.sulfurBlock = sulfurBlock;
|
||||
this.sourceLocation = sourceLocation;
|
||||
this.launchVelocity = launchVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Potent Sulfur block.
|
||||
*/
|
||||
public Block getSulfurBlock() {
|
||||
return this.sulfurBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location where the geyser exits the water column.
|
||||
*/
|
||||
public Location getSourceLocation() {
|
||||
return this.sourceLocation.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the velocity that will be added to the entity.
|
||||
* <br>
|
||||
* This value is read-only, changes made to it <b>will not</b> have any
|
||||
* effect on the final launch velocity.
|
||||
*/
|
||||
public Vector getLaunchVelocity() {
|
||||
return this.launchVelocity.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLER_LIST;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.papermc.paper.persistence;
|
||||
|
||||
import java.util.Set;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||
@@ -178,23 +177,6 @@ public interface PersistentDataContainerView {
|
||||
return this.getOrDefault(dataKey.key(), dataKey.dataType(), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the {@link PersistentDataType} of the value based on the primitive
|
||||
* type that is stored for the given key.
|
||||
* <br>
|
||||
* <b>WARNING:</b> This method will only return data types
|
||||
* defined in {@link PersistentDataType} which directly map to a specific
|
||||
* nbt tag. The return value will be wrong if the data type is plugin
|
||||
* specific or not represented by a specific nbt tag.
|
||||
* Use {@link #has(NamespacedKey, PersistentDataType)} if you expect a
|
||||
* specific data type.
|
||||
*
|
||||
* @param key the key to look up the value type of
|
||||
* @return estimated data type or null if no value is present, the type is
|
||||
* unknown or an empty list is encountered as value.
|
||||
*/
|
||||
@Nullable PersistentDataType<?, ?> getPrimitiveStorageType(Key key);
|
||||
|
||||
/**
|
||||
* Get the set of keys present on this {@link PersistentDataContainer}
|
||||
* instance.
|
||||
|
||||
@@ -961,7 +961,8 @@ public final class Bukkit {
|
||||
* Create a new explorer map targeting the closest nearby structure of a
|
||||
* given {@link StructureType}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
* This method uses implementation default values for radius and
|
||||
* findUnexplored (usually 100, true).
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
@@ -985,7 +986,8 @@ public final class Bukkit {
|
||||
* Create a new explorer map targeting the closest nearby structure of a
|
||||
* given {@link org.bukkit.generator.structure.StructureType}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
* This method uses implementation default values for radius and
|
||||
* findUnexplored (usually 100, true).
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
@@ -1019,45 +1021,6 @@ public final class Bukkit {
|
||||
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored) {
|
||||
return server.createExplorerMap(world, location, structureType, mapIcon, radius, findUnexplored);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby specific
|
||||
* {@link org.bukkit.generator.structure.Structure}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
* @param structure the specific structure to find
|
||||
* @param mapIcon the map icon to use on the map
|
||||
* @return a newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* org.bukkit.generator.structure.Structure, int, boolean)
|
||||
*/
|
||||
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.Structure structure, @NotNull org.bukkit.map.MapCursor.Type mapIcon) {
|
||||
return server.createExplorerMap(world, location, structure, mapIcon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby specific
|
||||
* {@link org.bukkit.generator.structure.Structure}.
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
* @param structure the specific structure to find
|
||||
* @param mapIcon the map icon to use on the map
|
||||
* @param radius radius to search, see World#locateNearestStructure for more
|
||||
* information
|
||||
* @param findUnexplored whether to find unexplored structures
|
||||
* @return the newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* org.bukkit.generator.structure.Structure, int, boolean)
|
||||
*/
|
||||
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.Structure structure, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored) {
|
||||
return server.createExplorerMap(world, location, structure, mapIcon, radius, findUnexplored);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,12 +6,10 @@ import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import net.kyori.adventure.util.ARGBLike;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.configuration.serialization.SerializableAs;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
/**
|
||||
* A container for a color palette. This class is immutable; the set methods
|
||||
@@ -19,7 +17,7 @@ import org.jetbrains.annotations.Range;
|
||||
* but subject to change.
|
||||
*/
|
||||
@SerializableAs("Color")
|
||||
public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
public final class Color implements ConfigurationSerializable {
|
||||
private static final int BIT_MASK = 0xff;
|
||||
private static final int DEFAULT_ALPHA = 255;
|
||||
|
||||
@@ -124,12 +122,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromARGB(
|
||||
final @Range(from = 0, to = 255) int alpha,
|
||||
final @Range(from = 0, to = 255) int red,
|
||||
final @Range(from = 0, to = 255) int green,
|
||||
final @Range(from = 0, to = 255) int blue
|
||||
) throws IllegalArgumentException {
|
||||
public static Color fromARGB(int alpha, int red, int green, int blue) throws IllegalArgumentException {
|
||||
return new Color(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
@@ -143,11 +136,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromRGB(
|
||||
final @Range(from = 0, to = 255) int red,
|
||||
final @Range(from = 0, to = 255) int green,
|
||||
final @Range(from = 0, to = 255) int blue
|
||||
) throws IllegalArgumentException {
|
||||
public static Color fromRGB(int red, int green, int blue) throws IllegalArgumentException {
|
||||
return new Color(DEFAULT_ALPHA, red, green, blue);
|
||||
}
|
||||
|
||||
@@ -161,11 +150,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromBGR(
|
||||
final @Range(from = 0, to = 255) int blue,
|
||||
final @Range(from = 0, to = 255) int green,
|
||||
final @Range(from = 0, to = 255) int red
|
||||
) throws IllegalArgumentException {
|
||||
public static Color fromBGR(int blue, int green, int red) throws IllegalArgumentException {
|
||||
return new Color(DEFAULT_ALPHA, red, green, blue);
|
||||
}
|
||||
|
||||
@@ -179,7 +164,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* bits
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromRGB(final int rgb) throws IllegalArgumentException {
|
||||
public static Color fromRGB(int rgb) throws IllegalArgumentException {
|
||||
Preconditions.checkArgument((rgb >> 24) == 0, "Extraneous data in: %s", rgb);
|
||||
return fromRGB(rgb >> 16 & BIT_MASK, rgb >> 8 & BIT_MASK, rgb & BIT_MASK);
|
||||
}
|
||||
@@ -192,7 +177,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @return a new color object for specified values
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromARGB(final int argb) {
|
||||
public static Color fromARGB(int argb) {
|
||||
return fromARGB(argb >> 24 & BIT_MASK, argb >> 16 & BIT_MASK, argb >> 8 & BIT_MASK, argb & BIT_MASK);
|
||||
}
|
||||
|
||||
@@ -206,16 +191,16 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* bits
|
||||
*/
|
||||
@NotNull
|
||||
public static Color fromBGR(final int bgr) throws IllegalArgumentException {
|
||||
public static Color fromBGR(int bgr) throws IllegalArgumentException {
|
||||
Preconditions.checkArgument((bgr >> 24) == 0, "Extrenuous data in: %s", bgr);
|
||||
return fromBGR(bgr >> 16 & BIT_MASK, bgr >> 8 & BIT_MASK, bgr & BIT_MASK);
|
||||
}
|
||||
|
||||
private Color(final int red, final int green, final int blue) {
|
||||
private Color(int red, int green, int blue) {
|
||||
this(DEFAULT_ALPHA, red, green, blue);
|
||||
}
|
||||
|
||||
private Color(final int alpha, final int red, final int green, final int blue) {
|
||||
private Color(int alpha, int red, int green, int blue) {
|
||||
Preconditions.checkArgument(alpha >= 0 && alpha <= 0xff, "Alpha[%s] is not between 0-255", alpha);
|
||||
Preconditions.checkArgument(red >= 0 && red <= 0xff, "Red[%s] is not between 0-255", red);
|
||||
Preconditions.checkArgument(green >= 0 && green <= 0xff, "Green[%s] is not between 0-255", green);
|
||||
@@ -228,123 +213,83 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alpha component.
|
||||
* Gets the alpha component
|
||||
*
|
||||
* @return alpha component, from 0 to 255
|
||||
*/
|
||||
public @Range(from = 0, to = 255) int getAlpha() {
|
||||
public int getAlpha() {
|
||||
return BIT_MASK & alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alpha component.
|
||||
*
|
||||
* @return alpha component, from 0 to 255
|
||||
*/
|
||||
@Override
|
||||
public @Range(from = 0, to = 255) int alpha() {
|
||||
return this.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Color object with a specified component.
|
||||
* Creates a new Color object with specified component
|
||||
*
|
||||
* @param alpha the alpha component, from 0 to 255
|
||||
* @return a new color object with the alpha component
|
||||
*/
|
||||
@NotNull
|
||||
public Color setAlpha(final @Range(from = 0, to = 255) int alpha) {
|
||||
return fromARGB(alpha, this.getRed(), this.getGreen(), this.getBlue());
|
||||
public Color setAlpha(int alpha) {
|
||||
return fromARGB(alpha, getRed(), getGreen(), getBlue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the red component.
|
||||
* Gets the red component
|
||||
*
|
||||
* @return red component, from 0 to 255
|
||||
*/
|
||||
public @Range(from = 0, to = 255) int getRed() {
|
||||
return BIT_MASK & this.red;
|
||||
public int getRed() {
|
||||
return BIT_MASK & red;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the red component.
|
||||
*
|
||||
* @return red component, from 0 to 255
|
||||
*/
|
||||
@Override
|
||||
public @Range(from = 0, to = 255) int red() {
|
||||
return this.getRed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Color object with a specified component.
|
||||
* Creates a new Color object with specified component
|
||||
*
|
||||
* @param red the red component, from 0 to 255
|
||||
* @return a new color object with the red component
|
||||
*/
|
||||
@NotNull
|
||||
public Color setRed(final @Range(from = 0, to = 255) int red) {
|
||||
return fromARGB(this.getAlpha(), red, this.getGreen(), this.getBlue());
|
||||
public Color setRed(int red) {
|
||||
return fromARGB(getAlpha(), red, getGreen(), getBlue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the green component.
|
||||
* Gets the green component
|
||||
*
|
||||
* @return green component, from 0 to 255
|
||||
*/
|
||||
public @Range(from = 0, to = 255) int getGreen() {
|
||||
return BIT_MASK & this.green;
|
||||
public int getGreen() {
|
||||
return BIT_MASK & green;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the green component.
|
||||
*
|
||||
* @return green component, from 0 to 255
|
||||
*/
|
||||
@Override
|
||||
public @Range(from = 0, to = 255) int green() {
|
||||
return this.getGreen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Color object with a specified component.
|
||||
* Creates a new Color object with specified component
|
||||
*
|
||||
* @param green the green component, from 0 to 255
|
||||
* @return a new color object with the green component
|
||||
*/
|
||||
@NotNull
|
||||
public Color setGreen(final @Range(from = 0, to = 255) int green) {
|
||||
return fromARGB(this.getAlpha(), this.getRed(), green, this.getBlue());
|
||||
public Color setGreen(int green) {
|
||||
return fromARGB(getAlpha(), getRed(), green, getBlue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blue component.
|
||||
* Gets the blue component
|
||||
*
|
||||
* @return blue component, from 0 to 255
|
||||
*/
|
||||
public @Range(from = 0, to = 255) int getBlue() {
|
||||
return BIT_MASK & this.blue;
|
||||
public int getBlue() {
|
||||
return BIT_MASK & blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blue component.
|
||||
*
|
||||
* @return blue component, from 0 to 255
|
||||
*/
|
||||
@Override
|
||||
public @Range(from = 0, to = 255) int blue() {
|
||||
return this.getBlue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Color object with a specified component.
|
||||
* Creates a new Color object with specified component
|
||||
*
|
||||
* @param blue the blue component, from 0 to 255
|
||||
* @return a new color object with the blue component
|
||||
*/
|
||||
@NotNull
|
||||
public Color setBlue(final @Range(from = 0, to = 255) int blue) {
|
||||
return fromARGB(this.getAlpha(), this.getRed(), this.getGreen(), blue);
|
||||
public Color setBlue(int blue) {
|
||||
return fromARGB(getAlpha(), getRed(), getGreen(), blue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,7 +298,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @return An integer representation of this color, as 0xRRGGBB
|
||||
*/
|
||||
public int asRGB() {
|
||||
return this.getRed() << 16 | this.getGreen() << 8 | this.getBlue();
|
||||
return getRed() << 16 | getGreen() << 8 | getBlue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +307,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @return An integer representation of this color, as 0xAARRGGBB
|
||||
*/
|
||||
public int asARGB() {
|
||||
return this.getAlpha() << 24 | this.getRed() << 16 | this.getGreen() << 8 | this.getBlue();
|
||||
return getAlpha() << 24 | getRed() << 16 | getGreen() << 8 | getBlue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,33 +316,33 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
* @return An integer representation of this color, as 0xBBGGRR
|
||||
*/
|
||||
public int asBGR() {
|
||||
return this.getBlue() << 16 | this.getGreen() << 8 | this.getRed();
|
||||
return getBlue() << 16 | getGreen() << 8 | getRed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new color with its RGB components changed as if it was dyed
|
||||
* with the colors passed in, replicating vanilla workbench dyeing.
|
||||
* with the colors passed in, replicating vanilla workbench dyeing
|
||||
*
|
||||
* @param colors The DyeColors to dye with
|
||||
* @return A new color with the changed rgb components
|
||||
*/
|
||||
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
|
||||
@NotNull
|
||||
public Color mixDyes(final @NotNull DyeColor... colors) {
|
||||
public Color mixDyes(@NotNull DyeColor... colors) {
|
||||
Preconditions.checkArgument(colors != null && Arrays.stream(colors).allMatch(Objects::nonNull), "DyeColor cannot be null or contain null values");
|
||||
|
||||
final Color[] toPass = new Color[colors.length];
|
||||
Color[] toPass = new Color[colors.length];
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
toPass[i] = colors[i].getColor();
|
||||
}
|
||||
|
||||
return this.mixColors(toPass);
|
||||
return mixColors(toPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new color with its RGB components changed as if it was dyed
|
||||
* with the colors passed in, replicating vanilla workbench dyeing.
|
||||
* <br>
|
||||
*
|
||||
* <b>Note that this method does not currently take into account alpha
|
||||
* components.</b>
|
||||
*
|
||||
@@ -406,7 +351,7 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
*/
|
||||
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
|
||||
@NotNull
|
||||
public Color mixColors(final @NotNull Color... colors) {
|
||||
public Color mixColors(@NotNull Color... colors) {
|
||||
Preconditions.checkArgument(colors != null && Arrays.stream(colors).allMatch(Objects::nonNull), "Colors cannot be null");
|
||||
|
||||
int totalRed = this.getRed();
|
||||
@@ -420,43 +365,44 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
totalMax += Math.max(Math.max(color.getRed(), color.getGreen()), color.getBlue());
|
||||
}
|
||||
|
||||
final float averageRed = totalRed / (colors.length + 1);
|
||||
final float averageGreen = totalGreen / (colors.length + 1);
|
||||
final float averageBlue = totalBlue / (colors.length + 1);
|
||||
final float averageMax = totalMax / (colors.length + 1);
|
||||
float averageRed = totalRed / (colors.length + 1);
|
||||
float averageGreen = totalGreen / (colors.length + 1);
|
||||
float averageBlue = totalBlue / (colors.length + 1);
|
||||
float averageMax = totalMax / (colors.length + 1);
|
||||
|
||||
final float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
|
||||
final float gainFactor = averageMax / maximumOfAverages;
|
||||
float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
|
||||
float gainFactor = averageMax / maximumOfAverages;
|
||||
|
||||
return Color.fromRGB((int) (averageRed * gainFactor), (int) (averageGreen * gainFactor), (int) (averageBlue * gainFactor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!(o instanceof Color that)) {
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Color)) {
|
||||
return false;
|
||||
}
|
||||
final Color that = (Color) o;
|
||||
return this.alpha == that.alpha && this.blue == that.blue && this.green == that.green && this.red == that.red;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.asARGB() ^ Color.class.hashCode();
|
||||
return asARGB() ^ Color.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Map<String, Object> serialize() {
|
||||
return ImmutableMap.of(
|
||||
"ALPHA", this.getAlpha(),
|
||||
"RED", this.getRed(),
|
||||
"BLUE", this.getBlue(),
|
||||
"GREEN", this.getGreen()
|
||||
"ALPHA", getAlpha(),
|
||||
"RED", getRed(),
|
||||
"BLUE", getBlue(),
|
||||
"GREEN", getGreen()
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Color deserialize(final @NotNull Map<String, Object> map) {
|
||||
public static Color deserialize(@NotNull Map<String, Object> map) {
|
||||
return fromARGB(
|
||||
asInt("ALPHA", map, DEFAULT_ALPHA),
|
||||
asInt("RED", map),
|
||||
@@ -465,23 +411,23 @@ public final class Color implements ConfigurationSerializable, ARGBLike {
|
||||
);
|
||||
}
|
||||
|
||||
private static int asInt(final @NotNull String string, final @NotNull Map<String, Object> map) {
|
||||
private static int asInt(@NotNull String string, @NotNull Map<String, Object> map) {
|
||||
return asInt(string, map, null);
|
||||
}
|
||||
|
||||
private static int asInt(final @NotNull String string, final @NotNull Map<String, Object> map, final @Nullable Object defaultValue) {
|
||||
final Object value = map.getOrDefault(string, defaultValue);
|
||||
private static int asInt(@NotNull String string, @NotNull Map<String, Object> map, @Nullable Object defaultValue) {
|
||||
Object value = map.getOrDefault(string, defaultValue);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException(string + " not in map " + map);
|
||||
}
|
||||
if (!(value instanceof Number number)) {
|
||||
if (!(value instanceof Number)) {
|
||||
throw new IllegalArgumentException(string + '(' + value + ") is not a number");
|
||||
}
|
||||
return number.intValue();
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Color:[argb0x" + Integer.toHexString(this.asARGB()).toUpperCase(Locale.ROOT) + "]";
|
||||
return "Color:[argb0x" + Integer.toHexString(asARGB()).toUpperCase(Locale.ROOT) + "]";
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,6 @@ import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scoreboard.Criteria;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
import org.bukkit.generator.structure.Structure;
|
||||
import org.bukkit.map.MapCursor;
|
||||
import org.bukkit.structure.StructureManager;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -634,7 +632,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
* @param spawnCategory the category of spawn
|
||||
* @return the default ticks per {@link SpawnCategory} mobs spawn value
|
||||
* @throws IllegalArgumentException if the category is {@link SpawnCategory#MISC}
|
||||
* @deprecated this is configured per world; use {@link World#getTicksPerSpawns(SpawnCategory)}.
|
||||
* This returns the primary world's value.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "26.3")
|
||||
public int getTicksPerSpawns(@NotNull SpawnCategory spawnCategory);
|
||||
|
||||
/**
|
||||
@@ -874,7 +875,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
* Create a new explorer map targeting the closest nearby structure of a
|
||||
* given {@link StructureType}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
* This method uses implementation default values for radius and
|
||||
* findUnexplored (usually 100, true).
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
@@ -883,9 +885,9 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* org.bukkit.StructureType, int, boolean)
|
||||
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, MapCursor.Type)}
|
||||
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, org.bukkit.map.MapCursor.Type)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Deprecated // Paper
|
||||
@NotNull
|
||||
default ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType) {
|
||||
return this.createExplorerMap(world, location, structureType, 100, true);
|
||||
@@ -905,16 +907,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* org.bukkit.StructureType, int, boolean)
|
||||
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, MapCursor.Type, int, boolean)}
|
||||
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, org.bukkit.map.MapCursor.Type, int, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
@NotNull ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType, int radius, boolean findUnexplored);
|
||||
|
||||
@Deprecated // Paper
|
||||
@NotNull
|
||||
public ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType, int radius, boolean findUnexplored);
|
||||
// Paper start
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby structure of a
|
||||
* given {@link StructureType}.
|
||||
* given {@link org.bukkit.generator.structure.StructureType}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
* This method uses implementation default values for radius and
|
||||
* findUnexplored (usually 100, true).
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
@@ -923,15 +927,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
* @return a newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* StructureType, int, boolean)
|
||||
* org.bukkit.generator.structure.StructureType, int, boolean)
|
||||
*/
|
||||
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull MapCursor.Type mapIcon) {
|
||||
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon) {
|
||||
return this.createExplorerMap(world, location, structureType, mapIcon, 100, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby structure of a
|
||||
* given {@link StructureType}.
|
||||
* given {@link org.bukkit.generator.structure.StructureType}.
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
@@ -943,46 +947,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
* @return the newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* StructureType, int, boolean)
|
||||
* org.bukkit.generator.structure.StructureType, int, boolean)
|
||||
*/
|
||||
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull MapCursor.Type mapIcon, int radius, boolean findUnexplored);
|
||||
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby specific
|
||||
* {@link Structure}.
|
||||
* <br>
|
||||
* This method uses a radius of 100 and only searches for unexplored structures.
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
* @param structure the specific structure to find
|
||||
* @param mapIcon the map icon to use on the map
|
||||
* @return a newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* Structure, int, boolean)
|
||||
*/
|
||||
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull Structure structure, @NotNull MapCursor.Type mapIcon) {
|
||||
return this.createExplorerMap(world, location, structure, mapIcon, 100, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new explorer map targeting the closest nearby specific
|
||||
* {@link Structure}.
|
||||
*
|
||||
* @param world the world the map will belong to
|
||||
* @param location the origin location to find the nearest structure
|
||||
* @param structure the specific structure to find
|
||||
* @param mapIcon the map icon to use on the map
|
||||
* @param radius radius to search, see World#locateNearestStructure for more
|
||||
* information
|
||||
* @param findUnexplored whether to find unexplored structures
|
||||
* @return the newly created item stack or null if it can't find a location
|
||||
*
|
||||
* @see World#locateNearestStructure(org.bukkit.Location,
|
||||
* Structure, int, boolean)
|
||||
*/
|
||||
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull Structure structure, @NotNull MapCursor.Type mapIcon, int radius, boolean findUnexplored);
|
||||
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored);
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Reloads the server, refreshing settings and plugin information.
|
||||
@@ -1943,7 +1911,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*
|
||||
* @param spawnCategory the category spawn
|
||||
* @return the {@link SpawnCategory} spawn limit
|
||||
* @deprecated this is configured per world; use {@link World#getSpawnLimit(SpawnCategory)}.
|
||||
* This returns the primary world's value.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "26.3")
|
||||
int getSpawnLimit(@NotNull SpawnCategory spawnCategory);
|
||||
|
||||
/**
|
||||
@@ -2456,60 +2427,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
// Spigot start
|
||||
public class Spigot {
|
||||
|
||||
/**
|
||||
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
|
||||
* wherever possible, rather than directly reading from a server config.
|
||||
*
|
||||
* @see #getServerConfig()
|
||||
* @return The server's spigot config.
|
||||
*/
|
||||
@Deprecated(since = "1.21.4", forRemoval = true)
|
||||
@NotNull
|
||||
public org.bukkit.configuration.file.YamlConfiguration getConfig() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
|
||||
* wherever possible, rather than directly reading from a server config.
|
||||
*
|
||||
* @see #getServerConfig()
|
||||
* @return The server's bukkit config.
|
||||
*/
|
||||
// Paper start
|
||||
@Deprecated(since = "1.21.4", forRemoval = true)
|
||||
@NotNull
|
||||
public org.bukkit.configuration.file.YamlConfiguration getBukkitConfig() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
|
||||
* wherever possible, rather than directly reading from a server config.
|
||||
*
|
||||
* @see #getServerConfig()
|
||||
* @return The server's spigot config.
|
||||
*/
|
||||
@Deprecated(since = "1.21.4", forRemoval = true)
|
||||
@NotNull
|
||||
public org.bukkit.configuration.file.YamlConfiguration getSpigotConfig() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
|
||||
* wherever possible, rather than directly reading from a server config.
|
||||
*
|
||||
* @see #getServerConfig()
|
||||
* @return The server's paper config.
|
||||
*/
|
||||
@Deprecated(since = "1.21.4", forRemoval = true)
|
||||
@NotNull
|
||||
public org.bukkit.configuration.file.YamlConfiguration getPaperConfig() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Sends the component to the player
|
||||
*
|
||||
@@ -2556,6 +2473,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*/
|
||||
void restart();
|
||||
|
||||
@Deprecated(forRemoval = true, since = "26.3")
|
||||
void reloadPermissions(); // Paper
|
||||
|
||||
boolean reloadCommandAliases(); // Paper
|
||||
@@ -2576,7 +2494,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
* @deprecated use {@link #permissionMessage()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
@Deprecated(forRemoval = true)
|
||||
String getPermissionMessage();
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,15 +110,7 @@ public enum TreeType {
|
||||
*/
|
||||
PALE_OAK_CREAKING,
|
||||
/**
|
||||
* Poplar tree with red leaves
|
||||
* Poplar tree
|
||||
*/
|
||||
RED_POPLAR,
|
||||
/**
|
||||
* Poplar tree with orange leaves
|
||||
*/
|
||||
ORANGE_POPLAR,
|
||||
/**
|
||||
* Poplar tree with yellow leaves
|
||||
*/
|
||||
YELLOW_POPLAR,
|
||||
POPLAR,
|
||||
}
|
||||
@@ -3472,7 +3472,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
|
||||
@@ -3499,7 +3499,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)}
|
||||
@@ -3526,7 +3526,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* this world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
|
||||
@@ -3553,7 +3553,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* this world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
|
||||
@@ -3580,7 +3580,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* in this world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
|
||||
@@ -3607,7 +3607,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* this world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param limit the new mob limit
|
||||
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
|
||||
@@ -3631,7 +3631,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
|
||||
* this world
|
||||
* <p>
|
||||
* <b>Note:</b> If set to a negative number the world will use the
|
||||
* server-wide spawn limit instead.
|
||||
* spawn limit from its configuration instead.
|
||||
*
|
||||
* @param spawnCategory the entity category
|
||||
* @param limit the new mob limit
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
package org.bukkit.block;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.block.sign.Side;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.material.Colorable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a captured state of either a SignPost or a WallSign.
|
||||
*/
|
||||
@NullMarked
|
||||
public interface Sign extends TileState, Colorable {
|
||||
|
||||
// Paper start
|
||||
/**
|
||||
* Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
|
||||
*
|
||||
* @return List of components containing each line of text
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default List<Component> lines() {
|
||||
return this.getSide(Side.FRONT).lines();
|
||||
}
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public java.util.List<net.kyori.adventure.text.Component> lines();
|
||||
|
||||
/**
|
||||
* Gets the line of text at the specified index on the {@link Side#FRONT}.
|
||||
@@ -36,14 +29,13 @@ public interface Sign extends TileState, Colorable {
|
||||
* For example, getLine(0) will return the first line of text.
|
||||
*
|
||||
* @param index Line number to get the text from, starting at 0
|
||||
* @return Text on the given line
|
||||
* @throws IndexOutOfBoundsException Thrown when the line does not exist
|
||||
* @return Text on the given line
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default Component line(int index) throws IndexOutOfBoundsException {
|
||||
return this.getSide(Side.FRONT).line(index);
|
||||
}
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Sets the line of text at the specified index on the {@link Side#FRONT}.
|
||||
@@ -52,25 +44,23 @@ public interface Sign extends TileState, Colorable {
|
||||
* "Line One".
|
||||
*
|
||||
* @param index Line number to set the text at, starting from 0
|
||||
* @param line New text to set at the specified index
|
||||
* @param line New text to set at the specified index
|
||||
* @throws IndexOutOfBoundsException If the index is out of the range 0..3
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, Component)}.
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default void line(int index, Component line) throws IndexOutOfBoundsException {
|
||||
this.getSide(Side.FRONT).line(index, line);
|
||||
}
|
||||
@Deprecated
|
||||
public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
|
||||
*
|
||||
* @return Array of Strings containing each line of text
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default String[] getLines() {
|
||||
return this.getSide(Side.FRONT).getLines();
|
||||
}
|
||||
@NotNull
|
||||
public String[] getLines();
|
||||
|
||||
/**
|
||||
* Gets the line of text at the specified index.
|
||||
@@ -83,9 +73,8 @@ public interface Sign extends TileState, Colorable {
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default String getLine(int index) throws IndexOutOfBoundsException {
|
||||
return this.getSide(Side.FRONT).getLine(index);
|
||||
}
|
||||
@NotNull
|
||||
public String getLine(int index) throws IndexOutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Sets the line of text at the specified index.
|
||||
@@ -94,14 +83,12 @@ public interface Sign extends TileState, Colorable {
|
||||
* "Line One".
|
||||
*
|
||||
* @param index Line number to set the text at, starting from 0
|
||||
* @param line New text to set at the specified index
|
||||
* @param line New text to set at the specified index
|
||||
* @throws IndexOutOfBoundsException If the index is out of the range 0..3
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default void setLine(int index, String line) throws IndexOutOfBoundsException {
|
||||
this.getSide(Side.FRONT).setLine(index, line);
|
||||
}
|
||||
public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Marks whether this sign can be edited by players.
|
||||
@@ -110,9 +97,7 @@ public interface Sign extends TileState, Colorable {
|
||||
* @deprecated use {@link #isWaxed()} instead
|
||||
*/
|
||||
@Deprecated(since = "1.20.1")
|
||||
default boolean isEditable() {
|
||||
return !this.isWaxed();
|
||||
}
|
||||
public boolean isEditable();
|
||||
|
||||
/**
|
||||
* Marks whether this sign can be edited by players.
|
||||
@@ -121,9 +106,7 @@ public interface Sign extends TileState, Colorable {
|
||||
* @deprecated use {@link #setWaxed(boolean)} instead
|
||||
*/
|
||||
@Deprecated(since = "1.20.1")
|
||||
default void setEditable(boolean editable) {
|
||||
this.setWaxed(!editable);
|
||||
}
|
||||
public void setEditable(boolean editable);
|
||||
|
||||
/**
|
||||
* Gets whether or not this sign has been waxed. If a sign has been waxed, it
|
||||
@@ -131,7 +114,7 @@ public interface Sign extends TileState, Colorable {
|
||||
*
|
||||
* @return if this sign is waxed
|
||||
*/
|
||||
boolean isWaxed();
|
||||
public boolean isWaxed();
|
||||
|
||||
/**
|
||||
* Sets whether or not this sign has been waxed. If a sign has been waxed, it
|
||||
@@ -139,7 +122,7 @@ public interface Sign extends TileState, Colorable {
|
||||
*
|
||||
* @param waxed if this sign is waxed
|
||||
*/
|
||||
void setWaxed(boolean waxed);
|
||||
public void setWaxed(boolean waxed);
|
||||
|
||||
/**
|
||||
* Gets whether this sign has glowing text. Only affects the {@link Side#FRONT}.
|
||||
@@ -148,9 +131,7 @@ public interface Sign extends TileState, Colorable {
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#isGlowingText()}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default boolean isGlowingText() {
|
||||
return this.getSide(Side.FRONT).isGlowingText();
|
||||
}
|
||||
public boolean isGlowingText();
|
||||
|
||||
/**
|
||||
* Sets whether this sign has glowing text. Only affects the {@link Side#FRONT}.
|
||||
@@ -159,20 +140,17 @@ public interface Sign extends TileState, Colorable {
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#setGlowingText(boolean)}.
|
||||
*/
|
||||
@Deprecated(since = "1.20")
|
||||
default void setGlowingText(boolean glowing) {
|
||||
this.getSide(Side.FRONT).setGlowingText(glowing);
|
||||
}
|
||||
public void setGlowingText(boolean glowing);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#getColor()}.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
@Deprecated(since = "1.20")
|
||||
default @Nullable DyeColor getColor() {
|
||||
return this.getSide(Side.FRONT).getColor();
|
||||
}
|
||||
public DyeColor getColor();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@@ -181,9 +159,7 @@ public interface Sign extends TileState, Colorable {
|
||||
*/
|
||||
@Override
|
||||
@Deprecated(since = "1.20")
|
||||
default void setColor(DyeColor color) {
|
||||
this.getSide(Side.FRONT).setColor(color);
|
||||
}
|
||||
public void setColor(@NotNull DyeColor color);
|
||||
|
||||
/**
|
||||
* Return the side of the sign.
|
||||
@@ -191,7 +167,8 @@ public interface Sign extends TileState, Colorable {
|
||||
* @param side the side of the sign
|
||||
* @return the selected side of the sign
|
||||
*/
|
||||
SignSide getSide(Side side);
|
||||
@NotNull
|
||||
public SignSide getSide(@NotNull Side side);
|
||||
|
||||
/**
|
||||
* Gets the side of this sign the given player is currently standing on.
|
||||
@@ -199,7 +176,8 @@ public interface Sign extends TileState, Colorable {
|
||||
* @param player the player
|
||||
* @return the side the player is standing on
|
||||
*/
|
||||
SignSide getTargetSide(Player player);
|
||||
@NotNull
|
||||
public SignSide getTargetSide(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Gets the player that is currently allowed to edit this sign. <br>
|
||||
@@ -209,15 +187,16 @@ public interface Sign extends TileState, Colorable {
|
||||
*
|
||||
* @return the player allowed to edit this sign, or null
|
||||
*/
|
||||
@Nullable Player getAllowedEditor();
|
||||
|
||||
@Nullable
|
||||
public Player getAllowedEditor();
|
||||
// Paper start - More Sign Block API
|
||||
/**
|
||||
* Gets the allowed editor's UUID.
|
||||
* <br>Edits from other players will be rejected if this value is not null.
|
||||
*
|
||||
* @return the allowed editor's UUID, or null
|
||||
*/
|
||||
@Nullable UUID getAllowedEditorUniqueId();
|
||||
@Nullable java.util.UUID getAllowedEditorUniqueId();
|
||||
|
||||
/**
|
||||
* Sets the allowed editor's UUID.
|
||||
@@ -226,7 +205,7 @@ public interface Sign extends TileState, Colorable {
|
||||
*
|
||||
* @param uuid the allowed editor's UUID
|
||||
*/
|
||||
void setAllowedEditorUniqueId(@Nullable UUID uuid);
|
||||
void setAllowedEditorUniqueId(@Nullable java.util.UUID uuid);
|
||||
|
||||
/**
|
||||
* Compute the side facing the specified entity.
|
||||
@@ -234,7 +213,7 @@ public interface Sign extends TileState, Colorable {
|
||||
* @param entity the entity
|
||||
* @return the side it is facing
|
||||
*/
|
||||
default Side getInteractableSideFor(final Entity entity) {
|
||||
default @NotNull Side getInteractableSideFor(org.bukkit.entity.@NotNull Entity entity) {
|
||||
return this.getInteractableSideFor(entity.getLocation());
|
||||
}
|
||||
|
||||
@@ -244,7 +223,7 @@ public interface Sign extends TileState, Colorable {
|
||||
* @param position the position
|
||||
* @return the side the position is facing
|
||||
*/
|
||||
default Side getInteractableSideFor(final Position position) {
|
||||
default @NotNull Side getInteractableSideFor(io.papermc.paper.math.@NotNull Position position) {
|
||||
return this.getInteractableSideFor(position.x(), position.z());
|
||||
}
|
||||
|
||||
@@ -255,20 +234,6 @@ public interface Sign extends TileState, Colorable {
|
||||
* @param z the z coord
|
||||
* @return the side the coordinates are facing
|
||||
*/
|
||||
Side getInteractableSideFor(double x, double z);
|
||||
|
||||
/**
|
||||
* Gets whether operator features are allowed on this sign.
|
||||
* When enabled, supported click events in the root component of each line are executed when the relevant side is used.
|
||||
*
|
||||
* @return if operator features are allowed
|
||||
*/
|
||||
boolean isOperatorFeaturesAllowed();
|
||||
|
||||
/**
|
||||
* Sets whether operator features are allowed on this sign.
|
||||
*
|
||||
* @param allowed whether operator features are allowed
|
||||
*/
|
||||
void setOperatorFeaturesAllowed(boolean allowed);
|
||||
@NotNull Side getInteractableSideFor(double x, double z);
|
||||
// Paper end - More Sign Block API
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.bukkit.entity;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -1402,39 +1401,25 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
|
||||
void setHurtDirection(float hurtDirection);
|
||||
// Paper end - hurt direction API
|
||||
|
||||
// Paper start - swing hand API
|
||||
/**
|
||||
* Makes this entity swing their hand, using the {@link io.papermc.paper.datacomponent.DataComponentTypes#ATTACK_ANIMATION}
|
||||
* component on the item held in that hand to determine the animation type and duration.
|
||||
* If the held item does not have the component, the default {@link io.papermc.paper.datacomponent.item.SwingAnimation.Animation#WHACK}
|
||||
* animation with a duration of {@code 6} ticks is used.
|
||||
* Makes this entity swing their hand.
|
||||
*
|
||||
* <p>This method does nothing if this entity does not
|
||||
* have an animation for swinging their hand.
|
||||
*
|
||||
* @param hand hand to be swung, either {@link org.bukkit.inventory.EquipmentSlot#HAND} or {@link org.bukkit.inventory.EquipmentSlot#OFF_HAND}
|
||||
* @throws IllegalArgumentException if an invalid hand is passed
|
||||
* @throws IllegalArgumentException if invalid hand is passed
|
||||
*/
|
||||
default void swingHand(@NotNull org.bukkit.inventory.EquipmentSlot hand) {
|
||||
Preconditions.checkArgument(hand != null && hand.isHand(), "Expected a valid hand, got \"%s\" instead!", hand);
|
||||
com.google.common.base.Preconditions.checkArgument(hand != null && hand.isHand(), String.format("Expected a valid hand, got \"%s\" instead!", hand));
|
||||
if (hand == org.bukkit.inventory.EquipmentSlot.HAND) {
|
||||
this.swingMainHand();
|
||||
} else {
|
||||
this.swingOffHand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes this entity swing their hand using the provided {@link io.papermc.paper.datacomponent.item.SwingAnimation},
|
||||
* instead of the swing animation component the held item may have.
|
||||
*
|
||||
* <p>This method does nothing if this entity does not
|
||||
* have an animation for swinging their hand.
|
||||
*
|
||||
* @param hand hand to be swung, either {@link org.bukkit.inventory.EquipmentSlot#HAND} or {@link org.bukkit.inventory.EquipmentSlot#OFF_HAND}
|
||||
* @param swingAnimation the swing animation to use
|
||||
* @throws IllegalArgumentException if an invalid hand is passed
|
||||
*/
|
||||
void swingHand(@NotNull org.bukkit.inventory.EquipmentSlot hand, @NotNull io.papermc.paper.datacomponent.item.SwingAnimation swingAnimation);
|
||||
// Paper end - swing hand API
|
||||
|
||||
// Paper start - knockback API
|
||||
/**
|
||||
|
||||
@@ -30,8 +30,7 @@ public enum ClickType {
|
||||
*/
|
||||
WINDOW_BORDER_RIGHT,
|
||||
/**
|
||||
* The middle click action, bound to the middle mouse button by default.
|
||||
* This may also be triggered by another key if rebound by the client.
|
||||
* The middle mouse button, or a "scrollwheel click".
|
||||
*/
|
||||
MIDDLE,
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ plugins {
|
||||
val testData = sourceSets.create("testData")
|
||||
|
||||
dependencies {
|
||||
implementation("com.puppycrawl.tools:checkstyle:14.3.0")
|
||||
implementation("com.puppycrawl.tools:checkstyle:13.8.0")
|
||||
implementation("org.jspecify:jspecify:1.0.0")
|
||||
|
||||
testCompileOnly("org.jetbrains:annotations:26.1.0")
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@@ -162,16 +163,7 @@ public final class Util {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dotted name and the first identifier node it starts at.
|
||||
*
|
||||
* @param name the full dotted name
|
||||
* @param startNode the first identifier node of the name
|
||||
*/
|
||||
public record QualifiedName(String name, DetailAST startNode) {
|
||||
}
|
||||
|
||||
public static QualifiedName extractFullIdent(final DetailAST lastDot) {
|
||||
public static Pair<String, DetailAST> extractFullIdent(final DetailAST lastDot) {
|
||||
final List<String> parts = new ArrayList<>();
|
||||
DetailAST dot = lastDot;
|
||||
while (dot.getChildCount(TokenTypes.DOT) > 0) {
|
||||
@@ -188,6 +180,6 @@ public final class Util {
|
||||
}
|
||||
parts.addFirst(ident.getText());
|
||||
|
||||
return new QualifiedName(String.join(".", parts), ident);
|
||||
return Pair.of(String.join(".", parts), ident);
|
||||
}
|
||||
}
|
||||
+1
-7
@@ -6,9 +6,7 @@ import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
|
||||
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -21,7 +19,7 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
|
||||
|
||||
@Override
|
||||
public int[] getDefaultJavadocTokens() {
|
||||
return new int[] {JavadocCommentsTokenTypes.JAVADOC_CONTENT};
|
||||
return new int[]{JavadocCommentsTokenTypes.JAVADOC_CONTENT};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,7 +30,6 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
|
||||
final List<ParamDesc> params = new ArrayList<>();
|
||||
int maxColumn = -1;
|
||||
DetailNode child = detailNode.getFirstChild();
|
||||
final Set<Integer> visitedLines = new HashSet<>();
|
||||
for (final DetailNode javadocBlock : JavadocUtil.getAllNodesOfType(detailNode, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG)) {
|
||||
final DetailNode paramBlock = JavadocUtil.findFirstToken(javadocBlock, JavadocCommentsTokenTypes.PARAM_BLOCK_TAG);
|
||||
if (paramBlock == null) {
|
||||
@@ -44,9 +41,6 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
|
||||
}
|
||||
// iterate over all text nodes (multiline)
|
||||
for (final DetailNode textNode : JavadocUtil.getAllNodesOfType(paramDescription, JavadocCommentsTokenTypes.TEXT)) {
|
||||
if (!visitedLines.add(textNode.getLineNumber())) {
|
||||
continue;
|
||||
}
|
||||
final Matcher matcher = SPACE_PREFIX.matcher(textNode.getText());
|
||||
int paramDescColNum = textNode.getColumnNumber();
|
||||
if (matcher.find()) {
|
||||
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailNode;
|
||||
import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
|
||||
import com.puppycrawl.tools.checkstyle.utils.BlockCommentPosition;
|
||||
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
|
||||
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Checks that {@code @param} tags are in the same order as the parameters they document,
|
||||
* with type parameters after regular parameters (the order IntelliJ generates them in).
|
||||
* Only tags that move backward in the expected order are reported, unknown names are ignored.
|
||||
*/
|
||||
public final class JavadocParamTagOrderCheck extends AbstractJavadocCheck {
|
||||
|
||||
private static final int[] DECLARATIONS = {
|
||||
TokenTypes.METHOD_DEF,
|
||||
TokenTypes.CTOR_DEF,
|
||||
TokenTypes.COMPACT_CTOR_DEF,
|
||||
TokenTypes.CLASS_DEF,
|
||||
TokenTypes.INTERFACE_DEF,
|
||||
TokenTypes.RECORD_DEF,
|
||||
};
|
||||
|
||||
@Override
|
||||
public int[] getDefaultJavadocTokens() {
|
||||
return new int[] {JavadocCommentsTokenTypes.JAVADOC_CONTENT};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJavadocToken(final DetailNode detailNode) {
|
||||
final DetailAST declaration = documentedDeclaration(this.getBlockCommentAst());
|
||||
if (declaration == null) {
|
||||
return;
|
||||
}
|
||||
final List<String> expectedOrder = expectedParamOrder(declaration);
|
||||
int furthestIndex = -1;
|
||||
for (final DetailNode javadocBlock : JavadocUtil.getAllNodesOfType(detailNode, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG)) {
|
||||
final DetailNode paramBlock = JavadocUtil.findFirstToken(javadocBlock, JavadocCommentsTokenTypes.PARAM_BLOCK_TAG);
|
||||
if (paramBlock == null) {
|
||||
continue;
|
||||
}
|
||||
final DetailNode paramName = JavadocUtil.findFirstToken(paramBlock, JavadocCommentsTokenTypes.PARAMETER_NAME);
|
||||
if (paramName == null) {
|
||||
continue;
|
||||
}
|
||||
final int index = expectedOrder.indexOf(paramName.getText());
|
||||
if (index == -1) {
|
||||
continue;
|
||||
}
|
||||
if (index < furthestIndex) {
|
||||
this.log(
|
||||
paramName.getLineNumber(),
|
||||
paramName.getColumnNumber(),
|
||||
"@param tag for %s should be in declaration order, with type parameters last".formatted(paramName.getText())
|
||||
);
|
||||
} else {
|
||||
furthestIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable DetailAST documentedDeclaration(final DetailAST blockComment) {
|
||||
if (!BlockCommentPosition.isOnMethod(blockComment)
|
||||
&& !BlockCommentPosition.isOnConstructor(blockComment)
|
||||
&& !BlockCommentPosition.isOnCompactConstructor(blockComment)
|
||||
&& !BlockCommentPosition.isOnClass(blockComment)
|
||||
&& !BlockCommentPosition.isOnInterface(blockComment)
|
||||
&& !BlockCommentPosition.isOnRecord(blockComment)) {
|
||||
return null;
|
||||
}
|
||||
DetailAST parent = blockComment.getParent();
|
||||
while (parent != null && !TokenUtil.isOfType(parent, DECLARATIONS)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
private static List<String> expectedParamOrder(final DetailAST declaration) {
|
||||
final List<String> names = new ArrayList<>();
|
||||
if (declaration.getType() == TokenTypes.COMPACT_CTOR_DEF) {
|
||||
// compact constructors document the components of the enclosing record
|
||||
addRecordComponentNames(names, declaration.getParent().getParent());
|
||||
return names;
|
||||
}
|
||||
final DetailAST parameters = declaration.findFirstToken(TokenTypes.PARAMETERS);
|
||||
if (parameters != null) {
|
||||
for (final DetailAST parameter : Util.childrenIterator(parameters, TokenTypes.PARAMETER_DEF)) {
|
||||
names.add(identText(parameter));
|
||||
}
|
||||
}
|
||||
addRecordComponentNames(names, declaration);
|
||||
final DetailAST typeParameters = declaration.findFirstToken(TokenTypes.TYPE_PARAMETERS);
|
||||
if (typeParameters != null) {
|
||||
for (final DetailAST typeParameter : Util.childrenIterator(typeParameters, TokenTypes.TYPE_PARAMETER)) {
|
||||
names.add("<" + identText(typeParameter) + ">");
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static void addRecordComponentNames(final List<String> names, final DetailAST declaration) {
|
||||
final DetailAST recordComponents = declaration.findFirstToken(TokenTypes.RECORD_COMPONENTS);
|
||||
if (recordComponents == null) {
|
||||
return;
|
||||
}
|
||||
for (final DetailAST component : Util.childrenIterator(recordComponents, TokenTypes.RECORD_COMPONENT_DEF)) {
|
||||
names.add(identText(component));
|
||||
}
|
||||
}
|
||||
|
||||
private static String identText(final DetailAST ast) {
|
||||
final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
|
||||
return ident == null ? "" : ident.getText();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ public final class NullabilityAnnotationsCheck extends AbstractCheck {
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[] {
|
||||
return new int[]{
|
||||
TokenTypes.METHOD_DEF,
|
||||
TokenTypes.PARAMETER_DEF,
|
||||
TokenTypes.ANNOTATION_FIELD_DEF,
|
||||
|
||||
+3
-13
@@ -20,7 +20,7 @@ public class RedundantNullabilityCheck extends AbstractCheck {
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[] {
|
||||
return new int[]{
|
||||
TokenTypes.CLASS_DEF,
|
||||
TokenTypes.INTERFACE_DEF,
|
||||
TokenTypes.ANNOTATION_DEF,
|
||||
@@ -31,20 +31,10 @@ public class RedundantNullabilityCheck extends AbstractCheck {
|
||||
|
||||
@Override
|
||||
public void visitToken(final DetailAST ast) {
|
||||
final DetailAST nullMarkedAnnotation = NullabilityAnnotationsCheck.getNullMarkedAnnotation(ast);
|
||||
if (nullMarkedAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
final boolean pkgIsNullMarked = Util.isPackageInfoAnnotated(Path.of(this.getFilePath()), NullabilityAnnotationsCheck::isNullMarkedAnnotation);
|
||||
if (pkgIsNullMarked) {
|
||||
final DetailAST nullMarkedAnnotation = NullabilityAnnotationsCheck.getNullMarkedAnnotation(ast);
|
||||
if (pkgIsNullMarked && nullMarkedAnnotation != null) {
|
||||
this.log(nullMarkedAnnotation.getLineNo(), ast.getColumnNo() - 1, "Redundant NullMarked annotation");
|
||||
return;
|
||||
}
|
||||
for (DetailAST parentDef = Util.getEnclosingTypeDeclaration(ast); parentDef != null; parentDef = Util.getEnclosingTypeDeclaration(parentDef)) {
|
||||
if (NullabilityAnnotationsCheck.getNullMarkedAnnotation(parentDef) != null) {
|
||||
this.log(nullMarkedAnnotation.getLineNo(), ast.getColumnNo() - 1, "Redundant NullMarked annotation");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-242
@@ -1,26 +1,13 @@
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.FullIdent;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
|
||||
import io.papermc.checkstyle.Util;
|
||||
import io.papermc.checkstyle.checks.util.ImportAwareAbstractCheck;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Reports fully qualified type references that could be imported instead.
|
||||
* <p>
|
||||
* A reference is only reported if its simple name doesn't already refer to another type in the file,
|
||||
* either through an import, a type declared in the file, or an unqualified use of that name.
|
||||
* <p>
|
||||
* Requires that the codebase be set up to follow standard naming conventions.
|
||||
* <ul>
|
||||
* <li>packages are always all lowercase</li>
|
||||
@@ -29,35 +16,6 @@ import org.jspecify.annotations.Nullable;
|
||||
*/
|
||||
public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractCheck {
|
||||
|
||||
private static final String JAVA_LANG = "java.lang";
|
||||
|
||||
private static final int[] TYPE_CONTEXTS = {
|
||||
TokenTypes.TYPE,
|
||||
TokenTypes.TYPE_ARGUMENT,
|
||||
TokenTypes.TYPE_PARAMETER,
|
||||
TokenTypes.TYPE_UPPER_BOUNDS,
|
||||
TokenTypes.TYPE_LOWER_BOUNDS,
|
||||
TokenTypes.EXTENDS_CLAUSE,
|
||||
TokenTypes.PERMITS_CLAUSE,
|
||||
TokenTypes.IMPLEMENTS_CLAUSE,
|
||||
TokenTypes.LITERAL_THROWS,
|
||||
TokenTypes.LITERAL_NEW,
|
||||
TokenTypes.ANNOTATION,
|
||||
};
|
||||
|
||||
private final ThreadLocal<@Nullable FileScope> fileScope = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Type names visible in the file being checked, other than imports.
|
||||
*
|
||||
* @param packageName the package of the file
|
||||
* @param topLevelTypes simple names of top level types declared in the file
|
||||
* @param nestedTypes simple names of nested and local types declared in the file
|
||||
* @param unqualifiedTypes simple names of types referenced without qualification
|
||||
*/
|
||||
private record FileScope(String packageName, Set<String> topLevelTypes, Set<String> nestedTypes, Set<String> unqualifiedTypes) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getDefaultTokens() {
|
||||
return this.getRequiredTokens();
|
||||
@@ -70,7 +28,7 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
|
||||
|
||||
@Override
|
||||
public int[] getRequiredTokens() {
|
||||
return new int[] {
|
||||
return new int[]{
|
||||
TokenTypes.TYPE,
|
||||
TokenTypes.TYPE_ARGUMENT,
|
||||
TokenTypes.TYPE_PARAMETER,
|
||||
@@ -79,168 +37,55 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
|
||||
TokenTypes.EXTENDS_CLAUSE,
|
||||
TokenTypes.PERMITS_CLAUSE,
|
||||
TokenTypes.IMPLEMENTS_CLAUSE,
|
||||
TokenTypes.LITERAL_THROWS,
|
||||
TokenTypes.LITERAL_NEW,
|
||||
TokenTypes.ANNOTATION,
|
||||
TokenTypes.METHOD_CALL,
|
||||
TokenTypes.METHOD_REF,
|
||||
TokenTypes.LITERAL_CLASS,
|
||||
TokenTypes.DOT,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTree(final DetailAST rootAST) {
|
||||
super.beginTree(rootAST);
|
||||
String packageName = "";
|
||||
final Set<String> topLevelTypes = new HashSet<>();
|
||||
final Set<String> nestedTypes = new HashSet<>();
|
||||
final Set<String> unqualifiedTypes = new HashSet<>();
|
||||
final Deque<DetailAST> nodes = new ArrayDeque<>();
|
||||
for (DetailAST node = rootAST; node != null; node = node.getNextSibling()) {
|
||||
nodes.push(node);
|
||||
private static @Nullable DetailAST fromMethodCall(final DetailAST methodCall) {
|
||||
final DetailAST dot = methodCall.findFirstToken(TokenTypes.DOT);
|
||||
if (dot == null) {
|
||||
return null;
|
||||
}
|
||||
while (!nodes.isEmpty()) {
|
||||
final DetailAST node = nodes.pop();
|
||||
for (DetailAST child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
nodes.push(child);
|
||||
}
|
||||
if (node.getType() == TokenTypes.PACKAGE_DEF) {
|
||||
packageName = FullIdent.createFullIdent(node.getLastChild().getPreviousSibling()).getText();
|
||||
} else if (node.getType() == TokenTypes.IDENT) {
|
||||
final DetailAST parent = node.getParent();
|
||||
if (TokenUtil.isTypeDeclaration(parent.getType())) {
|
||||
final DetailAST declarationParent = parent.getParent();
|
||||
if (declarationParent == null || declarationParent.getType() == TokenTypes.COMPILATION_UNIT) {
|
||||
topLevelTypes.add(node.getText());
|
||||
} else {
|
||||
nestedTypes.add(node.getText());
|
||||
}
|
||||
} else if (isUnqualifiedTypeReference(node, parent)) {
|
||||
unqualifiedTypes.add(node.getText());
|
||||
}
|
||||
}
|
||||
final DetailAST nextDot = dot.findFirstToken(TokenTypes.DOT);
|
||||
if (nextDot == null) {
|
||||
return null;
|
||||
}
|
||||
this.fileScope.set(new FileScope(packageName, topLevelTypes, nestedTypes, unqualifiedTypes));
|
||||
final DetailAST ident = nextDot.findFirstToken(TokenTypes.IDENT);
|
||||
if (ident == null || Character.isLowerCase(ident.getText().charAt(0))) {
|
||||
// method call not on a type
|
||||
return null;
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishTree(final DetailAST rootAST) {
|
||||
super.finishTree(rootAST);
|
||||
this.fileScope.remove();
|
||||
}
|
||||
|
||||
private static boolean isUnqualifiedTypeReference(final DetailAST ident, final DetailAST parent) {
|
||||
if (TokenUtil.isOfType(parent, TYPE_CONTEXTS)) {
|
||||
return true;
|
||||
private static @Nullable DetailAST fromLiteralClass(final DetailAST literalClass) {
|
||||
final DetailAST dot = literalClass.getParent();
|
||||
if (dot.getType() != TokenTypes.DOT) {
|
||||
return null;
|
||||
}
|
||||
// first part of a dotted name or method reference, like Type.member or Type::member
|
||||
return Character.isUpperCase(ident.getText().charAt(0))
|
||||
&& TokenUtil.isOfType(parent, TokenTypes.DOT, TokenTypes.METHOD_REF)
|
||||
&& parent.getFirstChild() == ident;
|
||||
return dot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitToken(final DetailAST ast) {
|
||||
switch (ast.getType()) {
|
||||
case TokenTypes.METHOD_CALL -> {
|
||||
final DetailAST callee = ast.findFirstToken(TokenTypes.DOT);
|
||||
if (callee != null) {
|
||||
this.checkExpressionChain(callee, true);
|
||||
}
|
||||
}
|
||||
case TokenTypes.METHOD_REF -> this.checkExpressionChain(ast.getFirstChild(), false);
|
||||
case TokenTypes.DOT -> {
|
||||
if (isFieldAccess(ast)) {
|
||||
this.checkExpressionChain(ast, true);
|
||||
}
|
||||
}
|
||||
case TokenTypes.LITERAL_CLASS -> {
|
||||
final DetailAST dot = ast.getParent();
|
||||
if (dot.getType() == TokenTypes.DOT) {
|
||||
this.checkChildren(dot);
|
||||
}
|
||||
}
|
||||
default -> this.checkChildren(ast);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkChildren(final DetailAST ast) {
|
||||
// clauses like extends, implements, throws, and bounds can contain multiple types
|
||||
for (final DetailAST dot : Util.childrenIterator(ast, TokenTypes.DOT)) {
|
||||
this.checkQualifiedName(dot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the type qualifier in a dotted expression, like {@code java.util.Locale} in {@code java.util.Locale.ROOT}.
|
||||
*
|
||||
* @param chain the outermost node of the dotted expression
|
||||
* @param lastIsMember whether the last part of the chain is a member (field or method) and not part of the type
|
||||
*/
|
||||
private void checkExpressionChain(final DetailAST chain, final boolean lastIsMember) {
|
||||
final List<DetailAST> dots = new ArrayList<>();
|
||||
DetailAST node = chain;
|
||||
while (node.getType() == TokenTypes.DOT) {
|
||||
dots.addFirst(node);
|
||||
node = node.getFirstChild();
|
||||
}
|
||||
if (node.getType() != TokenTypes.IDENT) {
|
||||
// starts with this, super, a method call, etc.
|
||||
final DetailAST actualAst = switch (ast.getType()) {
|
||||
case TokenTypes.METHOD_CALL -> fromMethodCall(ast);
|
||||
case TokenTypes.LITERAL_CLASS -> fromLiteralClass(ast);
|
||||
default -> ast;
|
||||
};
|
||||
if (actualAst == null) {
|
||||
return;
|
||||
}
|
||||
final List<String> parts = new ArrayList<>();
|
||||
parts.add(node.getText());
|
||||
for (final DetailAST dot : dots) {
|
||||
final DetailAST name = Util.getNextSibling(dot.getFirstChild(), TokenTypes.IDENT);
|
||||
if (name == null) {
|
||||
// .class, .this, .new, etc.
|
||||
return;
|
||||
}
|
||||
parts.add(name.getText());
|
||||
}
|
||||
int packageEnd = 0;
|
||||
while (packageEnd < parts.size() && !Character.isUpperCase(parts.get(packageEnd).charAt(0))) {
|
||||
packageEnd++;
|
||||
}
|
||||
if (packageEnd == 0 || packageEnd == parts.size()) {
|
||||
// not qualified by a package, or no type at all
|
||||
final DetailAST dot = actualAst.findFirstToken(TokenTypes.DOT);
|
||||
if (dot == null) {
|
||||
return;
|
||||
}
|
||||
int typeEnd = packageEnd;
|
||||
while (typeEnd + 1 < parts.size() && Character.isUpperCase(parts.get(typeEnd + 1).charAt(0))) {
|
||||
typeEnd++;
|
||||
}
|
||||
if (lastIsMember && typeEnd == parts.size() - 1) {
|
||||
typeEnd--;
|
||||
}
|
||||
if (typeEnd < packageEnd) {
|
||||
// only a member, like local.inner.FIELD
|
||||
return;
|
||||
}
|
||||
this.checkQualifiedName(dots.get(typeEnd - 1));
|
||||
}
|
||||
|
||||
private static boolean isFieldAccess(final DetailAST dot) {
|
||||
final DetailAST parent = dot.getParent();
|
||||
if (TokenUtil.isOfType(parent, TokenTypes.DOT, TokenTypes.METHOD_CALL, TokenTypes.METHOD_REF) || TokenUtil.isOfType(parent, TYPE_CONTEXTS)) {
|
||||
return false;
|
||||
}
|
||||
for (DetailAST ancestor = parent; ancestor != null; ancestor = ancestor.getParent()) {
|
||||
if (ancestor.getType() == TokenTypes.EXPR) {
|
||||
return true;
|
||||
}
|
||||
if (TokenUtil.isOfType(ancestor, TYPE_CONTEXTS)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkQualifiedName(final DetailAST dot) {
|
||||
final Util.QualifiedName fullIdent = Util.extractFullIdent(dot);
|
||||
final DetailAST startNode = fullIdent.startNode();
|
||||
final String fullIdentName = fullIdent.name();
|
||||
final Pair<String, DetailAST> fullIdent = Util.extractFullIdent(dot);
|
||||
final DetailAST startNode = fullIdent.getRight();
|
||||
final String fullIdentName = fullIdent.getLeft();
|
||||
if (Character.isUpperCase(fullIdentName.charAt(0))) {
|
||||
// not a package
|
||||
return;
|
||||
@@ -251,7 +96,7 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
|
||||
do {
|
||||
// Check if we have an import for this exact type
|
||||
if (this.hasImportFor(typeName)) {
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Fully qualified import used: " + withoutConstants(fullIdentName));
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Fully qualified import used: " + fullIdentName);
|
||||
return;
|
||||
}
|
||||
previousTypeName = typeName;
|
||||
@@ -259,61 +104,9 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
|
||||
} while ((lastDotIdx = typeName.lastIndexOf('.')) != -1 && Character.isUpperCase(typeName.substring(0, lastDotIdx + 2).charAt(lastDotIdx + 1)));
|
||||
|
||||
final String outerSimpleName = previousTypeName.substring(previousTypeName.lastIndexOf('.') + 1);
|
||||
final String outerPackage = previousTypeName.substring(0, previousTypeName.lastIndexOf('.'));
|
||||
if (this.getFullImportBySimple(outerSimpleName) == null && !this.isNameTaken(outerSimpleName, outerPackage)) {
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Unnecessary fully qualified import (no conflict): " + withoutConstants(fullIdentName));
|
||||
final String fullImportBySimple = this.getFullImportBySimple(outerSimpleName);
|
||||
if (fullImportBySimple == null) {
|
||||
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Unnecessary fully qualified import (no conflict): " + fullIdentName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the simple name already refers to a different type in this file.
|
||||
*
|
||||
* @param simpleName the simple name of the outermost type
|
||||
* @param packageName the package of the outermost type
|
||||
* @return true if the type can't be referenced by its simple name
|
||||
*/
|
||||
private boolean isNameTaken(final String simpleName, final String packageName) {
|
||||
final FileScope scope = this.fileScope.get();
|
||||
if (scope == null) {
|
||||
return false;
|
||||
}
|
||||
final boolean samePackage = packageName.equals(scope.packageName());
|
||||
if (scope.nestedTypes().contains(simpleName)) {
|
||||
// a nested type shadows any other type with the same name
|
||||
return true;
|
||||
}
|
||||
if (scope.topLevelTypes().contains(simpleName)) {
|
||||
// a top level type with the same name is only the same type if it's in the same package
|
||||
return !samePackage;
|
||||
}
|
||||
if (scope.unqualifiedTypes().contains(simpleName)) {
|
||||
// unqualified names resolve to the same package or java.lang (imports are already handled)
|
||||
return !samePackage && !JAVA_LANG.equals(packageName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes trailing constants from a dotted name, like {@code ROOT} in {@code java.util.Locale.ROOT}.
|
||||
*
|
||||
* @param name the dotted name
|
||||
* @return the name without trailing constants
|
||||
*/
|
||||
private static String withoutConstants(final String name) {
|
||||
String result = name;
|
||||
int lastDot;
|
||||
while ((lastDot = result.lastIndexOf('.')) != -1 && isConstantName(result.substring(lastDot + 1))) {
|
||||
final String qualifier = result.substring(0, lastDot);
|
||||
if (!Character.isUpperCase(qualifier.charAt(qualifier.lastIndexOf('.') + 1))) {
|
||||
// the constant-like part is the outermost type, like java.util.UUID
|
||||
break;
|
||||
}
|
||||
result = qualifier;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isConstantName(final String name) {
|
||||
return name.length() > 1 && name.equals(name.toUpperCase(Locale.ROOT)) && name.chars().anyMatch(Character::isLetter);
|
||||
}
|
||||
}
|
||||
-1
@@ -43,7 +43,6 @@ public abstract class ImportAwareAbstractCheck extends AbstractCheck {
|
||||
@Override
|
||||
public void finishTree(final DetailAST rootAST) {
|
||||
this.imports.remove();
|
||||
this.simpleImports.remove();
|
||||
}
|
||||
|
||||
protected boolean hasImportFor(final String type) {
|
||||
|
||||
@@ -12,12 +12,12 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
|
||||
@@ -27,7 +27,6 @@ import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
import org.junit.platform.commons.support.AnnotationSupport;
|
||||
import org.junit.platform.commons.support.ReflectionSupport;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -69,11 +68,10 @@ public @interface CheckTest {
|
||||
assertFalse(builder.checks.isEmpty(), "CheckTestBuilder must have at least one check");
|
||||
final TreeWalker walker = new TreeWalker();
|
||||
walker.setModuleFactory(new PackageObjectFactory("io.papermc.checkstyle.checks", Util.class.getClassLoader()));
|
||||
final Method registerCheck = ReflectionSupport.findMethod(TreeWalker.class, "registerCheck", AbstractCheck.class).orElseThrow();
|
||||
for (final AbstractCheck check : builder.checks) {
|
||||
check.init();
|
||||
check.configure(new DefaultConfiguration("Test"));
|
||||
ReflectionSupport.invokeMethod(registerCheck, walker, check);
|
||||
MethodUtils.invokeMethod(walker, true, "registerCheck", check);
|
||||
}
|
||||
final Path filePath = Path.of("src", "testData", "java", checkTest.value().replace('.', File.separatorChar) + ".java");
|
||||
assertTrue(Files.exists(filePath), "File not found: " + filePath);
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import io.papermc.checkstyle.CheckTest;
|
||||
import io.papermc.checkstyle.CheckTestBuilder;
|
||||
|
||||
class TestJavadocParamTagOrderCheck {
|
||||
|
||||
@CheckTest("testdata.JavadocParamTagOrder")
|
||||
void test(final CheckTestBuilder builder) {
|
||||
builder
|
||||
.addCheck(new JavadocParamTagOrderCheck())
|
||||
// regular parameters swapped
|
||||
.addViolation("28:15: @param tag for a should be in declaration order, with type parameters last")
|
||||
// type parameter before regular parameter
|
||||
.addViolation("37:15: @param tag for a should be in declaration order, with type parameters last")
|
||||
// type parameters swapped
|
||||
.addViolation("47:15: @param tag for <T> should be in declaration order, with type parameters last")
|
||||
// constructor
|
||||
.addViolation("66:15: @param tag for a should be in declaration order, with type parameters last")
|
||||
// class type parameters
|
||||
.addViolation("75:15: @param tag for <A> should be in declaration order, with type parameters last")
|
||||
// record component after type parameter
|
||||
.addViolation("94:15: @param tag for a should be in declaration order, with type parameters last")
|
||||
// record components swapped
|
||||
.addViolation("111:15: @param tag for a should be in declaration order, with type parameters last")
|
||||
// compact constructor
|
||||
.addViolation("119:19: @param tag for a should be in declaration order, with type parameters last");
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package io.papermc.checkstyle.checks;
|
||||
|
||||
import io.papermc.checkstyle.CheckTest;
|
||||
import io.papermc.checkstyle.CheckTestBuilder;
|
||||
|
||||
class TestRedundantNullabilityCheck {
|
||||
|
||||
@CheckTest("testdata.RedundantNullability")
|
||||
void test(final CheckTestBuilder builder) {
|
||||
builder
|
||||
.addViolation("10:8: Redundant NullMarked annotation")
|
||||
.addViolation("14:8: Redundant NullMarked annotation")
|
||||
.addViolation("18:8: Redundant NullMarked annotation")
|
||||
.addViolation("22:8: Redundant NullMarked annotation")
|
||||
.addCheck(new RedundantNullabilityCheck());
|
||||
}
|
||||
}
|
||||
-26
@@ -32,32 +32,6 @@ class TestUnnecessaryFullyQualifiedImportCheck {
|
||||
.addViolation("66:5: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// class literal ".class"
|
||||
.addViolation("70:28: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// throws clause, multiple types
|
||||
.addViolation("74:39: Unnecessary fully qualified import (no conflict): java.io.IOException")
|
||||
.addViolation("74:60: Unnecessary fully qualified import (no conflict): java.io.UncheckedIOException")
|
||||
// type param with multiple bounds
|
||||
.addViolation("77:36: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
.addViolation("77:89: Unnecessary fully qualified import (no conflict): java.io.Serializable")
|
||||
// implements clause, multiple types
|
||||
.addViolation("80:57: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
|
||||
.addViolation("80:109: Unnecessary fully qualified import (no conflict): java.io.Serializable")
|
||||
// static field access
|
||||
.addViolation("84:36: Unnecessary fully qualified import (no conflict): java.util.Locale")
|
||||
.addViolation("85:35: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
|
||||
// method call through a field
|
||||
.addViolation("86:9: Unnecessary fully qualified import (no conflict): java.lang.System")
|
||||
// method call on a constant, reported without the constant
|
||||
.addViolation("87:29: Unnecessary fully qualified import (no conflict): java.util.Locale")
|
||||
// method and constructor references
|
||||
.addViolation("88:36: Unnecessary fully qualified import (no conflict): java.lang.Thread")
|
||||
.addViolation("89:41: Unnecessary fully qualified import (no conflict): java.util.ArrayList")
|
||||
// no violation for holder.inner.VALUE (91), java.util.Random conflicting with a declared type (103),
|
||||
// or testdata.imports.testclasses.SamePackage conflicting with an unqualified use (108)
|
||||
// reference to the declared type itself
|
||||
.addViolation("104:5: Unnecessary fully qualified import (no conflict): testdata.imports.UnnecessaryFullyQualifiedImport")
|
||||
// same package type, no conflict
|
||||
.addViolation("109:5: Unnecessary fully qualified import (no conflict): testdata.imports.SamePackage")
|
||||
// no violation for testdata.imports.Shadowed (115), the nested Shadowed hides the same package type
|
||||
.addCheck(new UnnecessaryFullyQualifiedImportCheck());
|
||||
}
|
||||
}
|
||||
@@ -45,26 +45,4 @@ public class JavadocParamAlignment {
|
||||
public void method_multiline_correct(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that does something
|
||||
*
|
||||
* @param a this is a description
|
||||
* with a {@link #method(String, String)} link tag
|
||||
* @param superLongParamName a description
|
||||
* blabla
|
||||
*/
|
||||
public void method_multiline_correct_with_link(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A method where line 2 starts with a link tag
|
||||
*
|
||||
* @param a this is a description
|
||||
* {@link #method(String, String)} is at line start
|
||||
* @param superLongParamName a description
|
||||
* blabla
|
||||
*/
|
||||
public void method_line2_starts_with_link(String a, String superLongParamName) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// @formatter:off
|
||||
package testdata;
|
||||
|
||||
/**
|
||||
* Class with type parameters.
|
||||
*
|
||||
* @param <A> first
|
||||
* @param <B> second
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
public class JavadocParamTagOrder<A, B> {
|
||||
|
||||
/**
|
||||
* Correct order.
|
||||
*
|
||||
* @param a first
|
||||
* @param b second
|
||||
* @param <T> type
|
||||
* @param <U> type
|
||||
*/
|
||||
public <T, U> void correct(final int a, final int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular parameters out of order.
|
||||
*
|
||||
* @param b second
|
||||
* @param a first
|
||||
*/
|
||||
public void swapped(final int a, final int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Type parameter before regular parameters.
|
||||
*
|
||||
* @param <T> type
|
||||
* @param a first
|
||||
*/
|
||||
public <T> void typeParamFirst(final int a) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Type parameters out of order.
|
||||
*
|
||||
* @param a first
|
||||
* @param <U> type
|
||||
* @param <T> type
|
||||
*/
|
||||
public <T, U> void typeParamsSwapped(final int a) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown names are ignored.
|
||||
*
|
||||
* @param a first
|
||||
* @param unknown not a parameter
|
||||
* @param b second
|
||||
*/
|
||||
public void unknown(final int a, final int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor out of order.
|
||||
*
|
||||
* @param b second
|
||||
* @param a first
|
||||
*/
|
||||
public JavadocParamTagOrder(final int a, final int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Class type parameters out of order.
|
||||
*
|
||||
* @param <B> second
|
||||
* @param <A> first
|
||||
*/
|
||||
interface Swapped<A, B> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Record components then type parameters.
|
||||
*
|
||||
* @param a first
|
||||
* @param b second
|
||||
* @param <T> type
|
||||
*/
|
||||
record CorrectRecord<T>(int a, int b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Record type parameter first.
|
||||
*
|
||||
* @param <T> type
|
||||
* @param a first
|
||||
*/
|
||||
record TypeParamFirstRecord<T>(int a) {
|
||||
|
||||
/**
|
||||
* Compact constructor ignores record type parameters.
|
||||
*
|
||||
* @param a first
|
||||
*/
|
||||
TypeParamFirstRecord {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact constructor out of order.
|
||||
*
|
||||
* @param b second
|
||||
* @param a first
|
||||
*/
|
||||
record CompactRecord(int a, int b) {
|
||||
|
||||
/**
|
||||
* Compact constructor out of order.
|
||||
*
|
||||
* @param b second
|
||||
* @param a first
|
||||
*/
|
||||
CompactRecord {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package testdata;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
public class RedundantNullability {
|
||||
|
||||
@NullMarked
|
||||
public static class OuterClass {
|
||||
|
||||
@NullMarked
|
||||
public static class RedundantInnerClass {
|
||||
}
|
||||
|
||||
@NullMarked
|
||||
public interface RedundantInnerInterface {
|
||||
}
|
||||
|
||||
@NullMarked
|
||||
public record RedundantInnerRecord() {
|
||||
}
|
||||
|
||||
@NullMarked
|
||||
public enum RedundantInnerEnum {
|
||||
}
|
||||
|
||||
public static class ValidInnerClass {
|
||||
}
|
||||
}
|
||||
|
||||
public static class NonAnnotatedOuter {
|
||||
|
||||
@NullMarked
|
||||
public static class ValidAnnotatedInner {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package testdata.imports;
|
||||
|
||||
public class SamePackage {
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package testdata.imports;
|
||||
|
||||
public class Shadowed {
|
||||
}
|
||||
-43
@@ -70,47 +70,4 @@ public class UnnecessaryFullyQualifiedImport {
|
||||
System.out.println(testdata.imports.testclasses.subpkg.OtherClass.class);
|
||||
System.out.println(OtherClass.class);
|
||||
}
|
||||
|
||||
public void throwsClause() throws java.io.IOException, java.io.UncheckedIOException {
|
||||
}
|
||||
|
||||
public <T extends OtherClass & testdata.imports.testclasses.subpkg.OtherInterface & java.io.Serializable> void bounds() {
|
||||
}
|
||||
|
||||
abstract static class MultipleInterfaces implements testdata.imports.testclasses.subpkg.OtherInterface, java.io.Serializable {
|
||||
}
|
||||
|
||||
public void expressions() {
|
||||
final Object fieldAccess = java.util.Locale.ENGLISH;
|
||||
final int importedField = testdata.imports.testclasses.subpkg.OtherClass.CONSTANT;
|
||||
java.lang.System.out.println();
|
||||
final String root = java.util.Locale.ROOT.toString();
|
||||
final Runnable methodRef = java.lang.Thread::yield;
|
||||
final Runnable constructorRef = java.util.ArrayList::new;
|
||||
final Holder holder = new Holder();
|
||||
final Object notAType = holder.inner.VALUE;
|
||||
}
|
||||
|
||||
static final class Holder {
|
||||
static final int VALUE = 0;
|
||||
Holder inner;
|
||||
}
|
||||
|
||||
// declared in this file, so java.util.Random needs to be qualified
|
||||
static final class Random {
|
||||
}
|
||||
|
||||
java.util.Random declaredConflict;
|
||||
testdata.imports.UnnecessaryFullyQualifiedImport self;
|
||||
|
||||
// SamePackage resolves to testdata.imports.SamePackage, so the other one needs to be qualified
|
||||
SamePackage samePackage;
|
||||
testdata.imports.testclasses.SamePackage otherPackage;
|
||||
testdata.imports.SamePackage qualifiedSamePackage;
|
||||
|
||||
// the nested Shadowed hides testdata.imports.Shadowed, so it needs to be qualified even though it's in the same package
|
||||
static final class Shadowed {
|
||||
}
|
||||
|
||||
testdata.imports.Shadowed shadowedSamePackage;
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
package testdata.imports.testclasses;
|
||||
|
||||
public class SamePackage {
|
||||
}
|
||||
-2
@@ -2,8 +2,6 @@ package testdata.imports.testclasses.subpkg;
|
||||
|
||||
public class OtherClass {
|
||||
|
||||
public static final int CONSTANT = 0;
|
||||
|
||||
public static class InnerClass {
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ plugins {
|
||||
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
||||
|
||||
dependencies {
|
||||
mache("io.papermc:mache:26.3+build.2")
|
||||
mache("io.papermc:mache:26.3+build.1")
|
||||
paperclip("io.papermc:paperclip:3.0.4")
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":paper-api"))
|
||||
implementation(platform("ca.spottedleaf.leafpile:bom:1.3.2"))
|
||||
implementation(platform("ca.spottedleaf.leafpile:bom:1.3.0"))
|
||||
implementation("ca.spottedleaf.leafpile:common")
|
||||
implementation("ca.spottedleaf.leafpile:concurrentutil")
|
||||
implementation("ca.spottedleaf.leafpile:converter")
|
||||
@@ -136,7 +136,7 @@ dependencies {
|
||||
implementation("io.netty:netty-codec-haproxy:4.2.16.Final") // Add support for proxy protocol
|
||||
implementation("org.apache.logging.log4j:log4j-iostreams:2.26.0")
|
||||
implementation("org.ow2.asm:asm-commons:9.9.1")
|
||||
implementation("org.spongepowered:configurate-yaml:4.2.0")
|
||||
implementation("org.spongepowered:configurate-core:4.2.0") // plus GeyserMC's yaml loader with comments in io.papermc.paper.configuration.yaml
|
||||
|
||||
// Deps that were previously in the API but have now been moved here for backwards compat, eventually to be removed
|
||||
runtimeOnly("commons-lang:commons-lang:2.6")
|
||||
@@ -285,10 +285,10 @@ fun TaskContainer.registerRunTask(
|
||||
.asFile
|
||||
javaLauncher.set(project.javaToolchains.launcherFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
@Suppress("UnstableApiUsage")
|
||||
vendor.set(JvmVendorSpec.JETBRAINS)
|
||||
// TODO - JB runtime 25 has issues with spark rn
|
||||
// vendor.set(JvmVendorSpec.JETBRAINS)
|
||||
})
|
||||
jvmArgs("-XX:+AllowEnhancedClassRedefinition", "--enable-native-access=ALL-UNNAMED")
|
||||
jvmArgs(/*"-XX:+AllowEnhancedClassRedefinition", */"--enable-native-access=ALL-UNNAMED")
|
||||
|
||||
if (rootProject.childProjects["test-plugin"] != null) {
|
||||
val testPluginJar = rootProject.project(":test-plugin").tasks.jar.flatMap { it.archiveFile }
|
||||
|
||||
@@ -34050,10 +34050,10 @@ index 0000000000000000000000000000000000000000..863a9785c84af6e0c961ae32f0102b5d
|
||||
+ }
|
||||
+}
|
||||
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
index 130615b796d9568c8fc6db0cc838784f21976411..e8043e52b64ed565128bb18240694ad078911eed 100644
|
||||
index 7050fd6efb6fec16ba5657e254c54de1c671c9a0..aa14dd369d5d2c5c843839f289afdc39c200f0c3 100644
|
||||
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
@@ -227,6 +227,11 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
|
||||
@@ -216,6 +216,11 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
|
||||
@Override
|
||||
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
|
||||
final int fromVersion, final int toVersion) {
|
||||
@@ -34065,7 +34065,7 @@ index 130615b796d9568c8fc6db0cc838784f21976411..e8043e52b64ed565128bb18240694ad0
|
||||
return (CompoundTag)dataFixer.update(
|
||||
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
|
||||
).getValue();
|
||||
@@ -268,4 +273,4 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
|
||||
@@ -257,4 +262,4 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
|
||||
public boolean addTicketForEnderPearls(final ServerLevel world) {
|
||||
return !world.paperConfig().misc.legacyEnderPearlBehavior;
|
||||
}
|
||||
@@ -34086,10 +34086,10 @@ index 06103ffdf599af91bc1d3baf89772a1acc93a21a..fc1f82d313acb8bf9bd03b1f09c68035
|
||||
return structureTemplate.save(new CompoundTag());
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index a9ee3ae3217ce7182cd8ab11ea455bbedca1c363..7bc5221da2707b53f0d710f80ea4bfa838195ad4 100644
|
||||
index d40863262dd7cf0e7c5667fd836e80d4f47e25aa..c4adc7266f41057bc93e8c34929c079c9c468d0b 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -294,6 +294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -293,6 +293,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
private final ServerClockManager clockManager;
|
||||
|
||||
public static <S extends MinecraftServer> S spin(final Function<Thread, S> factory) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -463,7 +463,7 @@ index f74837dc0be560a1601132594587bcfc22dcf778..c5a212e73c2efeb1f440778cb3f1fa6e
|
||||
public void tick() {
|
||||
super.tick();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 0de6f540c5192dd152d2e0a55d6b26eee0f5107f..afb2c249078f85579f37a772c41c8138c5c8c521 100644
|
||||
index bb61d4e4ecfe5bab6269fa3d4303dab95719d7bb..3d219969a55896b1495c54c0a12a25b49ba093f0 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -388,6 +388,15 @@ public abstract class Entity
|
||||
@@ -496,7 +496,7 @@ index 0de6f540c5192dd152d2e0a55d6b26eee0f5107f..afb2c249078f85579f37a772c41c8138
|
||||
SynchedEntityData.Builder entityDataBuilder = new SynchedEntityData.Builder(this);
|
||||
entityDataBuilder.define(DATA_SHARED_FLAGS_ID, (byte)0);
|
||||
entityDataBuilder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply());
|
||||
@@ -1169,6 +1185,10 @@ public abstract class Entity
|
||||
@@ -1168,6 +1184,10 @@ public abstract class Entity
|
||||
} else {
|
||||
if (moverType == MoverType.PISTON) {
|
||||
delta = this.limitPistonMovement(delta);
|
||||
@@ -507,7 +507,7 @@ index 0de6f540c5192dd152d2e0a55d6b26eee0f5107f..afb2c249078f85579f37a772c41c8138
|
||||
if (delta.equals(Vec3.ZERO)) {
|
||||
return;
|
||||
}
|
||||
@@ -1184,6 +1204,13 @@ public abstract class Entity
|
||||
@@ -1183,6 +1203,13 @@ public abstract class Entity
|
||||
this.stuckSpeedMultiplier = Vec3.ZERO;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
@@ -522,10 +522,10 @@ index 0de6f540c5192dd152d2e0a55d6b26eee0f5107f..afb2c249078f85579f37a772c41c8138
|
||||
delta = this.maybeBackOffFromEdge(delta, moverType);
|
||||
Vec3 movement = this.collide(delta);
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index db627b1d589e2c9d457b2f1b6429ea11849a1d3a..442bdb6b0bea65f4d2527eea43d2205cbaa4107a 100644
|
||||
index 62ccee5c76303b679d591714e64ee93c1502f378..0ef6c4cefb457c87f4565701817d2f9034dfa0d8 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3391,6 +3391,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
@@ -3380,6 +3380,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
protected void playAttackSound() {
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ index f46cca0467bb4da5511bc953ce5b43fa606bf978..445c5cafad71028171c1f26193966177
|
||||
+
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index a828576457e0bb932750dba507bdef7e8903e144..2f5ec35bf7f104e259f85d15b17c8064daa05a5e 100644
|
||||
index d327301693c70affc866d355a08efe935e3656b4..efcf399ced67201525ea6e6b3bd46b93b7e1823d 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -154,6 +154,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ and then catch exceptions and close if they fire.
|
||||
Part of this commit was authored by: Spottedleaf, sandtechnology
|
||||
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index 91c1ff8b8234900fbe044bbe0c3c10122c27a91b..e226a4b3ae089c3685ea7e5ae052d7a7e96c8d52 100644
|
||||
index f7814801138d9547e693ad51ba83cb2cb4ef750e..e6a674eb859af1e98fe25b644c1c5f70728128d0 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -64,7 +64,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
+3
-3
@@ -267,7 +267,7 @@ index 595db570b0ade9cad7de77d2ab7b10d9e68699df..ac095f59d9f17d8239d6493d1c1e3942
|
||||
+ // Paper end - Use Velocity cipher
|
||||
}
|
||||
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
|
||||
index e226a4b3ae089c3685ea7e5ae052d7a7e96c8d52..76596e87945f0b95af8032338a64817a8faed4d0 100644
|
||||
index e6a674eb859af1e98fe25b644c1c5f70728128d0..936200d7620e1fe83eb45ddbcff86b6b6ecaab8f 100644
|
||||
--- a/net/minecraft/network/Connection.java
|
||||
+++ b/net/minecraft/network/Connection.java
|
||||
@@ -796,10 +796,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -320,7 +320,7 @@ index e226a4b3ae089c3685ea7e5ae052d7a7e96c8d52..76596e87945f0b95af8032338a64817a
|
||||
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
|
||||
} else {
|
||||
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 100e7a2483f63497a1b757c77377262de7248996..42408dede2808447281b49f4006c1d8b03c10bf4 100644
|
||||
index c2b09a693daf8b4d3d274c07b3be37ba4fc1a483..2f3b58491ef7be8cd2e081f2c44869cd1e50e6a6 100644
|
||||
--- a/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -71,6 +71,10 @@ public class ServerConnectionListener {
|
||||
@@ -335,7 +335,7 @@ index 100e7a2483f63497a1b757c77377262de7248996..42408dede2808447281b49f4006c1d8b
|
||||
.add(
|
||||
new ServerBootstrap()
|
||||
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index ab21339beb36388bffabb289546bbfe5732df855..eea7dd596eb9ac75dee29d843d786493d44034f3 100644
|
||||
index dd2c5b7bd16ca17e253501fbc49cc21c8a06d603..a1855a257c3ac801bbd371ca4508af755550d737 100644
|
||||
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -256,9 +256,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
||||
|
||||
+2
-2
@@ -2321,7 +2321,7 @@ index 0000000000000000000000000000000000000000..505b5e39d0b4cf2a56c1c720f0c9ab01
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index a4e6225add32df93b0d6009d98ee322941b69938..e197ac0ee5e49d7768a9de889c972b3d590f7951 100644
|
||||
index 7fa69d76bf2c802b538a21af40898b8abb79629e..d9daa3ea8c4dc21bbe0d0ea9f0ece3f6c6777ba4 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -238,6 +238,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
@@ -2347,7 +2347,7 @@ index a4e6225add32df93b0d6009d98ee322941b69938..e197ac0ee5e49d7768a9de889c972b3d
|
||||
return toLevel.dimension() != Level.NETHER || this.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 2f5ec35bf7f104e259f85d15b17c8064daa05a5e..7cbf8592ec5792e458fa07987cf258c51f9e1e8a 100644
|
||||
index efcf399ced67201525ea6e6b3bd46b93b7e1823d..a1da101177a5a4fb2b4ba5e34be165daf4d147fb 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -2169,6 +2169,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
|
||||
@@ -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 07ba0aeefec186d7befe60c86dc5180e3131de51..482b428cab996c19664b57c341c55db5a66a6387 100644
|
||||
index 255e01251005a547088aba437ca27ce06fc996e5..487fcb8546ad24125c6573fb84f22a02752b35f1 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -930,7 +930,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -929,7 +929,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
public boolean saveAllChunks(final boolean silent, final boolean flush, final boolean force, final boolean close) {
|
||||
// Paper end - add close param
|
||||
@@ -17,7 +17,7 @@ index 07ba0aeefec186d7befe60c86dc5180e3131de51..482b428cab996c19664b57c341c55db5
|
||||
boolean result = false;
|
||||
|
||||
for (ServerLevel level : this.getAllLevels()) {
|
||||
@@ -942,6 +942,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -941,6 +941,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
result = true;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ index 07ba0aeefec186d7befe60c86dc5180e3131de51..482b428cab996c19664b57c341c55db5
|
||||
GameProfile singleplayerProfile = this.getSingleplayerProfile();
|
||||
this.storageSource.saveDataTag(this.worldData, singleplayerProfile == null ? null : singleplayerProfile.id());
|
||||
if (flush) {
|
||||
@@ -949,14 +956,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -948,14 +955,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
} else {
|
||||
this.savedDataStorage.scheduleSave();
|
||||
}
|
||||
@@ -48,7 +48,7 @@ index 07ba0aeefec186d7befe60c86dc5180e3131de51..482b428cab996c19664b57c341c55db5
|
||||
boolean result = this.saveAllChunks(silent, flush, force);
|
||||
this.warnOnLowDiskSpace();
|
||||
return result;
|
||||
@@ -1602,9 +1608,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1600,9 +1606,31 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
this.ticksUntilAutosave--;
|
||||
@@ -83,7 +83,7 @@ index 07ba0aeefec186d7befe60c86dc5180e3131de51..482b428cab996c19664b57c341c55db5
|
||||
ProfilerFiller profiler = Profiler.get();
|
||||
this.server.spark.executeMainThreadTasks(); // Paper - spark
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index e197ac0ee5e49d7768a9de889c972b3d590f7951..85ab77c3d1d5420df648fd173eaf5ad4f4775a80 100644
|
||||
index e4d38cfdac2b926afae3b1b96679b6ce45ea256a..ca793f3f7300148cd55d2e873af5b43d9f4c708f 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1470,6 +1470,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
|
||||
|
||||
@@ -20,10 +20,10 @@ index ba372f75dec3ee8b89c48df2cc646f071a47d25a..706f529a7d1064aad128b1ee8c82f631
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 482b428cab996c19664b57c341c55db5a66a6387..bd5efcf1dee77c204bf29ef4693ac54146e8d901 100644
|
||||
index 487fcb8546ad24125c6573fb84f22a02752b35f1..b675e9322234761a69e71e9e8b9d41364caf34b6 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1743,32 +1743,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1741,32 +1741,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return new ServerStatus.Players(maxPlayers, players.size(), sample);
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ index 482b428cab996c19664b57c341c55db5a66a6387..bd5efcf1dee77c204bf29ef4693ac541
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index b5503b25ef58c7b19592c2e90649c774b155a17b..ffbb54a8576877f2b477615d1e2f84457fdf31e1 100644
|
||||
index 2cec345fdabc8e01e378e66c35093dec1fcb7c2c..0ae23c90594d85e94d17d8114a21fc62b79219d3 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -5385,6 +5385,11 @@ public abstract class Entity
|
||||
@@ -5379,6 +5379,11 @@ public abstract class Entity
|
||||
this.getBukkitEntity().taskScheduler.retire();
|
||||
}
|
||||
// Paper end - Folia schedulers
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 15 Feb 2019 01:08:19 -0500
|
||||
Subject: [PATCH] Allow Saving of Oversized Chunks
|
||||
|
||||
The Minecraft World Region File format has a hard cap of 1MB per chunk.
|
||||
This is due to the fact that the header of the file format only allocates
|
||||
a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector.
|
||||
|
||||
This limit can be reached fairly easily with books, resulting in the chunk being unable
|
||||
to save to the world. Worse off, is that nothing printed when this occured, and silently
|
||||
performed a chunk rollback on next load.
|
||||
|
||||
This leads to security risk with duplication and is being actively exploited.
|
||||
|
||||
This patch catches the too large scenario, falls back and moves any large Entity
|
||||
or Tile Entity into a new compound, and this compound is saved into a different file.
|
||||
|
||||
On Chunk Load, we check for oversized status, and if so, we load the extra file and
|
||||
merge the Entities and Tile Entities from the oversized chunk back into the level to
|
||||
then be loaded as normal.
|
||||
|
||||
Once a chunk is returned back to normal size, the oversized flag will clear, and no
|
||||
extra data file will exist.
|
||||
|
||||
This fix maintains compatability with all existing Anvil Region Format tools as it
|
||||
does not alter the save format. They will just not know about the extra entities.
|
||||
|
||||
This fix also maintains compatability if someone switches server jars to one without
|
||||
this fix, as the data will remain in the oversized file. Once the server returns
|
||||
to a jar with this fix, the data will be restored.
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 7767e5420e3d2e569008747523d70147ea8d97fb..eddf7c3cef3b3209b47de97d93862937e9d2c82b 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -68,6 +68,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
this.info = info;
|
||||
this.path = path;
|
||||
this.version = version;
|
||||
+ this.initOversizedState(); // Paper
|
||||
if (!Files.isDirectory(externalFileDir)) {
|
||||
throw new IllegalArgumentException("Expected directory, got " + externalFileDir.toAbsolutePath());
|
||||
}
|
||||
@@ -463,4 +464,75 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
private interface CommitOp {
|
||||
void run() throws IOException;
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ private final byte[] oversized = new byte[1024];
|
||||
+ private int oversizedCount;
|
||||
+
|
||||
+ private synchronized void initOversizedState() throws IOException {
|
||||
+ Path metaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(metaFile)) {
|
||||
+ final byte[] read = Files.readAllBytes(metaFile);
|
||||
+ System.arraycopy(read, 0, oversized, 0, oversized.length);
|
||||
+ for (byte temp : oversized) {
|
||||
+ oversizedCount += temp;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static int getChunkIndex(int x, int z) {
|
||||
+ return (x & 31) + (z & 31) * 32;
|
||||
+ }
|
||||
+
|
||||
+ synchronized boolean isOversized(int x, int z) {
|
||||
+ return this.oversized[getChunkIndex(x, z)] == 1;
|
||||
+ }
|
||||
+
|
||||
+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
|
||||
+ final int offset = getChunkIndex(x, z);
|
||||
+ boolean previous = this.oversized[offset] == 1;
|
||||
+ this.oversized[offset] = (byte) (oversized ? 1 : 0);
|
||||
+ if (!previous && oversized) {
|
||||
+ oversizedCount++;
|
||||
+ } else if (!oversized && previous) {
|
||||
+ oversizedCount--;
|
||||
+ }
|
||||
+ if (previous && !oversized) {
|
||||
+ Path oversizedFile = getOversizedFile(x, z);
|
||||
+ if (Files.exists(oversizedFile)) {
|
||||
+ Files.delete(oversizedFile);
|
||||
+ }
|
||||
+ }
|
||||
+ if (oversizedCount > 0) {
|
||||
+ if (previous != oversized) {
|
||||
+ writeOversizedMeta();
|
||||
+ }
|
||||
+ } else if (previous) {
|
||||
+ Path oversizedMetaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(oversizedMetaFile)) {
|
||||
+ Files.delete(oversizedMetaFile);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void writeOversizedMeta() throws IOException {
|
||||
+ Files.write(this.getOversizedMetaFile(), this.oversized);
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedMetaFile() {
|
||||
+ return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + ".oversized.nbt");
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedFile(int x, int z) {
|
||||
+ return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
|
||||
+ }
|
||||
+
|
||||
+ synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException {
|
||||
+ Path file = getOversizedFile(x, z);
|
||||
+ try (DataInputStream out = new DataInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file)))) { // Paper - Optimize NBT reading/writing; FastNBTParser buffers internally
|
||||
+ return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead((java.io.DataInput) out); // Paper - Optimize NBT reading/writing
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index a78e27fa5733bdb21e06e89025fd23551b3912e7..939d9a660ca9c878d2c087bad07f64dab3c06fef 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage { // Paper - rewrite chunk system
|
||||
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
|
||||
public static final String ANVIL_EXTENSION = ".mca";
|
||||
private static final int MAX_CACHE_SIZE = 256;
|
||||
private final Long2ObjectLinkedOpenHashMap<Optional<RegionFile>> regionCache = new Long2ObjectLinkedOpenHashMap<>();
|
||||
@@ -229,11 +230,54 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
return Objects.requireNonNull(this.getRegionFile(pos, true));
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static void printOversizedLog(String msg, Path file, int x, int z) {
|
||||
+ LOGGER.error("{} ({} - {},{}) Go clean it up to remove this message. /minecraft:tp {} 128 {} - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.", msg, file.toString().replaceAll(".+[\\\\/]", ""), x, z, x << 4, z << 4);
|
||||
+ }
|
||||
+
|
||||
+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
|
||||
+ synchronized (regionfile) {
|
||||
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
|
||||
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x(), chunkCoordinate.z());
|
||||
+ CompoundTag chunk = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(datainputstream); // Paper - Optimize NBT reading/writing
|
||||
+ if (oversizedData == null) {
|
||||
+ return chunk;
|
||||
+ }
|
||||
+ CompoundTag oversizedLevel = oversizedData.getCompoundOrEmpty("Level");
|
||||
+
|
||||
+ mergeChunkList(chunk.getCompoundOrEmpty("Level"), oversizedLevel, "Entities", "Entities");
|
||||
+ mergeChunkList(chunk.getCompoundOrEmpty("Level"), oversizedLevel, "TileEntities", "TileEntities");
|
||||
+
|
||||
+ return chunk;
|
||||
+ } catch (Throwable t) {
|
||||
+ t.printStackTrace();
|
||||
+ throw t;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static void mergeChunkList(CompoundTag level, CompoundTag oversizedLevel, String key, String oversizedKey) {
|
||||
+ net.minecraft.nbt.ListTag levelList = level.getListOrEmpty(key);
|
||||
+ net.minecraft.nbt.ListTag oversizedList = oversizedLevel.getListOrEmpty(oversizedKey);
|
||||
+
|
||||
+ if (!oversizedList.isEmpty()) {
|
||||
+ levelList.addAll(oversizedList);
|
||||
+ level.put(key, levelList);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public @Nullable CompoundTag read(final ChunkPos pos) throws IOException {
|
||||
RegionFile region = this.getRegionFile(pos, false);
|
||||
if (region == null) {
|
||||
return null;
|
||||
}
|
||||
+ // Paper start
|
||||
+ if (region.isOversized(pos.x(), pos.z())) {
|
||||
+ printOversizedLog("Loading Oversized Chunk!", region.getPath(), pos.x(), pos.z());
|
||||
+ return readOversizedChunk(region, pos);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
|
||||
return regionChunkInputStream == null ? null : ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
|
||||
@@ -265,6 +309,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
} else {
|
||||
try (DataOutputStream output = region.getChunkDataOutputStream(pos)) {
|
||||
NbtIo.write(value, output);
|
||||
+ region.setOversized(pos.x(), pos.z(), false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+6
-5
@@ -8,7 +8,7 @@ This ensures at least a valid version of the chunk exists
|
||||
on disk, even if outdated
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 1077ff25ca0da7fb6f89dd2e16174f00b4986283..bd4b488cdf7a386bc458fad2ae659b77e60962ad 100644
|
||||
index b6c6bf9b20fee63b7e2a37d949bb1e55cef40de6..3ba442ac6b823e1c6a69398abc4f116da07c32c0 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
|
||||
@@ -19,7 +19,7 @@ index 1077ff25ca0da7fb6f89dd2e16174f00b4986283..bd4b488cdf7a386bc458fad2ae659b77
|
||||
private static final int SECTOR_BYTES = 4096;
|
||||
@VisibleForTesting
|
||||
protected static final int SECTOR_INTS = 1024;
|
||||
@@ -903,6 +904,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
@@ -451,6 +452,24 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ index 1077ff25ca0da7fb6f89dd2e16174f00b4986283..bd4b488cdf7a386bc458fad2ae659b77
|
||||
public void close() throws IOException {
|
||||
ByteBuffer result = ByteBuffer.wrap(this.buf, 0, this.count);
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 9f8b1d4235d2f268a523c028006114b61d8d67ef..7e815fee19ab2c283bf1fdc3d6a530d4101de3de 100644
|
||||
index c4f27907774f694c6bf2129ba14e5155c838a19b..a2dd62ced02de213ffe1244b893069b93011713c 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -95,11 +95,24 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
@@ -73,7 +73,7 @@ index 9f8b1d4235d2f268a523c028006114b61d8d67ef..7e815fee19ab2c283bf1fdc3d6a530d4
|
||||
|
||||
return writeData;
|
||||
}
|
||||
@@ -330,8 +343,17 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
@@ -307,9 +320,18 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
if (value == null) {
|
||||
region.clear(pos);
|
||||
} else {
|
||||
@@ -82,6 +82,7 @@ index 9f8b1d4235d2f268a523c028006114b61d8d67ef..7e815fee19ab2c283bf1fdc3d6a530d4
|
||||
+ DataOutputStream output = region.getChunkDataOutputStream(pos);
|
||||
+ try { // Paper - Only write if successful
|
||||
NbtIo.write(value, output);
|
||||
region.setOversized(pos.x(), pos.z(), false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
+ // Paper start - don't write garbage data to disk if writing serialization fails
|
||||
+ output.close();
|
||||
+ } catch (final RegionFileSizeException ex) {
|
||||
@@ -92,7 +93,7 @@ index 9f8b1d4235d2f268a523c028006114b61d8d67ef..7e815fee19ab2c283bf1fdc3d6a530d4
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,4 +401,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
@@ -357,4 +379,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
public RegionStorageInfo info() {
|
||||
return this.info;
|
||||
}
|
||||
File renamed without changes.
+1
-1
@@ -14,7 +14,7 @@ timestamp so that fs watchers can detect when RegionFiles are
|
||||
modified.
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
index 48353259f9a17937f71951706e0172b5b4957d56..e9dc38d934ede23f17e18893ee207d9fbbd26449 100644
|
||||
index 6dd6b4fb79870da6d3bf54dc813ae685237d1f0e..09205efff3a1a2042e67600510af270df9e46c67 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
@@ -1309,6 +1309,14 @@ public final class MoonriseRegionFileIO {
|
||||
+1
-1
@@ -5,7 +5,7 @@ Subject: [PATCH] Remove debug-like logging
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 2b5377da8fc3d8582c47809a5e4815d7b0293780..d920a4905d044925c4c4ab980fc238c9c489d5ac 100644
|
||||
index 951f5ab5a05dcedf323845f57fd061645466e563..19c7dffe5f9c45cd99cb9b14cae5ac908cae0afc 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -1761,7 +1761,7 @@ public class ServerGamePacketListenerImpl
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+845
@@ -0,0 +1,845 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sun, 2 Feb 2020 02:25:10 -0800
|
||||
Subject: [PATCH] Attempt to recalculate regionfile header if it is corrupt
|
||||
|
||||
Instead of trying to relocate the chunk, which is seems to never
|
||||
be the correct choice, so we end up duplicating or swapping chunks,
|
||||
we instead drop the current regionfile header and recalculate -
|
||||
hoping that at least then we don't swap chunks, and maybe recover
|
||||
them all.
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
index 09205efff3a1a2042e67600510af270df9e46c67..775d93d35855e394e5eccf0ad80eca43b1ed9562 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
||||
@@ -1547,7 +1547,7 @@ public final class MoonriseRegionFileIO {
|
||||
|
||||
public abstract void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException;
|
||||
|
||||
- public static record ReadData(ReadResult result, DataInputStream input, CompoundTag syncRead) {
|
||||
+ public static record ReadData(ReadResult result, DataInputStream input, CompoundTag syncRead, int recalculateCount) { // Paper - Attempt to recalculate regionfile header if it is corrupt
|
||||
public static enum ReadResult {
|
||||
NO_DATA,
|
||||
HAS_DATA,
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
index a6f9435a8e2a6c8cc05bd3873cb6fc0ec98973b8..d44fd9eea0ccb09c894d6431ede8a4c2aae85c5b 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionBitmap.java
|
||||
@@ -9,6 +9,27 @@ import java.util.BitSet;
|
||||
public class RegionBitmap {
|
||||
private final BitSet used = new BitSet();
|
||||
|
||||
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
|
||||
+ public final void copyFrom(RegionBitmap other) {
|
||||
+ BitSet thisBitset = this.used;
|
||||
+ BitSet otherBitset = other.used;
|
||||
+
|
||||
+ for (int i = 0; i < Math.max(thisBitset.size(), otherBitset.size()); ++i) {
|
||||
+ thisBitset.set(i, otherBitset.get(i));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public final boolean tryAllocate(int from, int length) {
|
||||
+ BitSet bitset = this.used;
|
||||
+ int firstSet = bitset.nextSetBit(from);
|
||||
+ if (firstSet > 0 && firstSet < (from + length)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ bitset.set(from, from + length);
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
|
||||
+
|
||||
public void force(final int position, final int size) {
|
||||
this.used.set(position, position + size);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index d1ea7f442e258b06f5c879de7d57fd16caf09178..5e64762059846f14d4e8ddfec1d1640c199cece2 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -46,6 +46,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
@VisibleForTesting
|
||||
protected final RegionBitmap usedSectors = new RegionBitmap();
|
||||
|
||||
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
|
||||
+ private static long roundToSectors(long bytes) {
|
||||
+ long sectors = bytes >>> 12; // 4096 = 2^12
|
||||
+ long remainingBytes = bytes & 4095;
|
||||
+ long sign = -remainingBytes; // sign is 1 if nonzero
|
||||
+ return sectors + (sign >>> 63);
|
||||
+ }
|
||||
+
|
||||
+ private static final net.minecraft.nbt.CompoundTag OVERSIZED_COMPOUND = new net.minecraft.nbt.CompoundTag();
|
||||
+
|
||||
+ private net.minecraft.nbt.@Nullable CompoundTag attemptRead(long sector, int chunkDataLength, long fileLength) throws IOException {
|
||||
+ try {
|
||||
+ if (chunkDataLength < 0) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ long offset = sector * 4096L + 4L; // offset for chunk data
|
||||
+
|
||||
+ if ((offset + chunkDataLength) > fileLength) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ByteBuffer chunkData = ByteBuffer.allocate(chunkDataLength);
|
||||
+ if (chunkDataLength != this.file.read(chunkData, offset)) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ((java.nio.Buffer)chunkData).flip();
|
||||
+
|
||||
+ byte compressionType = chunkData.get();
|
||||
+ if (compressionType < 0) { // compressionType & 128 != 0
|
||||
+ // oversized chunk
|
||||
+ return OVERSIZED_COMPOUND;
|
||||
+ }
|
||||
+
|
||||
+ RegionFileVersion compression = RegionFileVersion.fromId(compressionType);
|
||||
+ if (compression == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position()));
|
||||
+
|
||||
+ return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(new DataInputStream(input)); // Paper - Optimize NBT reading/writing
|
||||
+ } catch (Exception ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private int getLength(long sector) throws IOException {
|
||||
+ ByteBuffer length = ByteBuffer.allocate(4);
|
||||
+ if (4 != this.file.read(length, sector * 4096L)) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return length.getInt(0);
|
||||
+ }
|
||||
+
|
||||
+ private void backupRegionFile() {
|
||||
+ Path backup = this.path.getParent().resolve(this.path.getFileName() + "." + new java.util.Random().nextLong() + ".backup");
|
||||
+ this.backupRegionFile(backup);
|
||||
+ }
|
||||
+
|
||||
+ private void backupRegionFile(Path to) {
|
||||
+ try {
|
||||
+ this.file.force(true);
|
||||
+ LOGGER.warn("Backing up regionfile \"{}\" to {}", this.path.toAbsolutePath(), to.toAbsolutePath());
|
||||
+ Files.copy(this.path, to, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
|
||||
+ LOGGER.warn("Backed up the regionfile to {}", to.toAbsolutePath());
|
||||
+ } catch (IOException ex) {
|
||||
+ LOGGER.error("Failed to backup to {}", to.toAbsolutePath(), ex);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean inSameRegionfile(ChunkPos first, ChunkPos second) {
|
||||
+ return (first.x() & ~31) == (second.x() & ~31) && (first.z() & ~31) == (second.z() & ~31);
|
||||
+ }
|
||||
+
|
||||
+ // note: only call for CHUNK regionfiles
|
||||
+ private final java.util.concurrent.atomic.AtomicInteger recalculateCount = new java.util.concurrent.atomic.AtomicInteger();
|
||||
+
|
||||
+ public int getRecalculateCount() {
|
||||
+ return this.recalculateCount.get();
|
||||
+ }
|
||||
+
|
||||
+ boolean recalculateHeader() throws IOException {
|
||||
+ if (!this.canRecalcHeader) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ ChunkPos ourLowerLeftPosition = RegionFileStorage.getRegionFileCoordinates(this.path);
|
||||
+ if (ourLowerLeftPosition == null) {
|
||||
+ LOGGER.error("Unable to get chunk location of regionfile {}, cannot recover header", this.path.toAbsolutePath());
|
||||
+ return false;
|
||||
+ }
|
||||
+ synchronized (this) {
|
||||
+ this.recalculateCount.getAndIncrement();
|
||||
+
|
||||
+ LOGGER.warn("Corrupt regionfile header detected! Attempting to re-calculate header offsets for regionfile {}", this.path.toAbsolutePath(), new Throwable());
|
||||
+
|
||||
+ // try to backup file so maybe it could be sent to us for further investigation
|
||||
+
|
||||
+ this.backupRegionFile();
|
||||
+ net.minecraft.nbt.CompoundTag[] compounds = new net.minecraft.nbt.CompoundTag[32 * 32]; // only in the regionfile (i.e exclude mojang/aikar oversized data)
|
||||
+ int[] rawLengths = new int[32 * 32]; // length of chunk data including 4 byte length field, bytes
|
||||
+ int[] sectorOffsets = new int[32 * 32]; // in sectors
|
||||
+ boolean[] hasAikarOversized = new boolean[32 * 32];
|
||||
+
|
||||
+ long fileLength = this.file.size();
|
||||
+ long totalSectors = roundToSectors(fileLength);
|
||||
+
|
||||
+ // search the regionfile from start to finish for the most up-to-date chunk data
|
||||
+
|
||||
+ for (long i = 2, maxSector = Math.min((long)(Integer.MAX_VALUE >>> 8), totalSectors); i < maxSector; ++i) { // first two sectors are header, skip
|
||||
+ int chunkDataLength = this.getLength(i);
|
||||
+ net.minecraft.nbt.CompoundTag compound = this.attemptRead(i, chunkDataLength, fileLength);
|
||||
+ if (compound == null || compound == OVERSIZED_COMPOUND) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ChunkPos chunkPos = SerializableChunkData.getChunkCoordinate(compound);
|
||||
+ if (!inSameRegionfile(ourLowerLeftPosition, chunkPos)) {
|
||||
+ LOGGER.error("Ignoring absolute chunk {} in regionfile as it is not contained in the bounds of the regionfile '{}'. It should be in regionfile ({},{})", chunkPos, this.path.toAbsolutePath(), chunkPos.x() >> 5, chunkPos.z() >> 5);
|
||||
+ continue;
|
||||
+ }
|
||||
+ int location = (chunkPos.x() & 31) | ((chunkPos.z() & 31) << 5);
|
||||
+
|
||||
+ net.minecraft.nbt.CompoundTag otherCompound = compounds[location];
|
||||
+
|
||||
+ if (otherCompound != null && SerializableChunkData.getLastWorldSaveTime(otherCompound) > SerializableChunkData.getLastWorldSaveTime(compound)) {
|
||||
+ continue; // don't overwrite newer data.
|
||||
+ }
|
||||
+
|
||||
+ // aikar oversized?
|
||||
+ Path aikarOversizedFile = this.getOversizedFile(chunkPos.x(), chunkPos.z());
|
||||
+ boolean isAikarOversized = false;
|
||||
+ if (Files.exists(aikarOversizedFile)) {
|
||||
+ try {
|
||||
+ net.minecraft.nbt.CompoundTag aikarOversizedCompound = this.getOversizedData(chunkPos.x(), chunkPos.z());
|
||||
+ if (SerializableChunkData.getLastWorldSaveTime(compound) == SerializableChunkData.getLastWorldSaveTime(aikarOversizedCompound)) {
|
||||
+ // best we got for an id. hope it's good enough
|
||||
+ isAikarOversized = true;
|
||||
+ }
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to read aikar oversized data for absolute chunk ({},{}) in regionfile {}, oversized data for this chunk will be lost", chunkPos.x(), chunkPos.z(), this.path.toAbsolutePath(), ex);
|
||||
+ // fall through, if we can't read aikar oversized we can't risk corrupting chunk data
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ hasAikarOversized[location] = isAikarOversized;
|
||||
+ compounds[location] = compound;
|
||||
+ rawLengths[location] = chunkDataLength + 4;
|
||||
+ sectorOffsets[location] = (int)i;
|
||||
+
|
||||
+ int chunkSectorLength = (int)roundToSectors(rawLengths[location]);
|
||||
+ i += chunkSectorLength;
|
||||
+ --i; // gets incremented next iteration
|
||||
+ }
|
||||
+
|
||||
+ // forge style oversized data is already handled by the local search, and aikar data we just hope
|
||||
+ // we get it right as aikar data has no identifiers we could use to try and find its corresponding
|
||||
+ // local data compound
|
||||
+
|
||||
+ java.nio.file.Path containingFolder = this.externalFileDir;
|
||||
+ Path[] regionFiles = Files.list(containingFolder).toArray(Path[]::new);
|
||||
+ boolean[] oversized = new boolean[32 * 32];
|
||||
+ RegionFileVersion[] oversizedCompressionTypes = new RegionFileVersion[32 * 32];
|
||||
+
|
||||
+ if (regionFiles != null) {
|
||||
+ int lowerXBound = ourLowerLeftPosition.x(); // inclusive
|
||||
+ int lowerZBound = ourLowerLeftPosition.z(); // inclusive
|
||||
+ int upperXBound = lowerXBound + 32 - 1; // inclusive
|
||||
+ int upperZBound = lowerZBound + 32 - 1; // inclusive
|
||||
+
|
||||
+ // read mojang oversized data
|
||||
+ for (Path regionFile : regionFiles) {
|
||||
+ ChunkPos oversizedCoords = getOversizedChunkPair(regionFile);
|
||||
+ if (oversizedCoords == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if ((oversizedCoords.x() < lowerXBound || oversizedCoords.x() > upperXBound) || (oversizedCoords.z() < lowerZBound || oversizedCoords.z() > upperZBound)) {
|
||||
+ continue; // not in our regionfile
|
||||
+ }
|
||||
+
|
||||
+ // ensure oversized data is valid & is newer than data in the regionfile
|
||||
+
|
||||
+ int location = (oversizedCoords.x() & 31) | ((oversizedCoords.z() & 31) << 5);
|
||||
+
|
||||
+ byte[] chunkData;
|
||||
+ try {
|
||||
+ chunkData = Files.readAllBytes(regionFile);
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to read oversized chunk data in file {}, data will be lost", regionFile.toAbsolutePath(), ex);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ net.minecraft.nbt.CompoundTag compound = null;
|
||||
+
|
||||
+ // We do not know the compression type, as it's stored in the regionfile. So we need to try all of them
|
||||
+ RegionFileVersion compression = null;
|
||||
+ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) {
|
||||
+ try {
|
||||
+ DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java
|
||||
+ compound = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead((java.io.DataInput)in); // Paper - Optimize NBT reading/writing
|
||||
+ compression = compressionType;
|
||||
+ break; // reaches here iff readNBT does not throw
|
||||
+ } catch (Exception ex) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (compound == null) {
|
||||
+ LOGGER.error("Failed to read oversized chunk data in file {}, it's corrupt. Its data will be lost", regionFile.toAbsolutePath());
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!SerializableChunkData.getChunkCoordinate(compound).equals(oversizedCoords)) {
|
||||
+ LOGGER.error("Can't use oversized chunk stored in {}, got absolute chunkpos: {}, expected {}", regionFile.toAbsolutePath(), net.minecraft.world.level.chunk.storage.SerializableChunkData.getChunkCoordinate(compound), oversizedCoords);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (compounds[location] == null || SerializableChunkData.getLastWorldSaveTime(compound) > SerializableChunkData.getLastWorldSaveTime(compounds[location])) {
|
||||
+ oversized[location] = true;
|
||||
+ oversizedCompressionTypes[location] = compression;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // now we need to calculate a new offset header
|
||||
+
|
||||
+ int[] calculatedOffsets = new int[32 * 32];
|
||||
+ RegionBitmap newSectorAllocations = new RegionBitmap();
|
||||
+ newSectorAllocations.force(0, 2); // make space for header
|
||||
+
|
||||
+ // allocate sectors for normal chunks
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ if (oversized[location]) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int rawLength = rawLengths[location]; // bytes
|
||||
+ int sectorOffset = sectorOffsets[location]; // sectors
|
||||
+ int sectorLength = (int)roundToSectors(rawLength);
|
||||
+
|
||||
+ if (newSectorAllocations.tryAllocate(sectorOffset, sectorLength)) {
|
||||
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
|
||||
+ } else {
|
||||
+ LOGGER.error("Failed to allocate space for local chunk (overlapping data??) at ({},{}) in regionfile {}, chunk will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // allocate sectors for oversized chunks
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ if (!oversized[location]) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int sectorOffset = newSectorAllocations.allocate(1);
|
||||
+ int sectorLength = 1;
|
||||
+
|
||||
+ try {
|
||||
+ this.file.write(this.createExternalStub(oversizedCompressionTypes[location]), sectorOffset * 4096);
|
||||
+ // only allocate in the new offsets if the write succeeds
|
||||
+ calculatedOffsets[location] = sectorOffset << 8 | (sectorLength > 255 ? 255 : sectorLength); // support forge style oversized
|
||||
+ } catch (IOException ex) {
|
||||
+ newSectorAllocations.free(sectorOffset, sectorLength);
|
||||
+ LOGGER.error("Failed to write new oversized chunk data holder, local chunk at ({},{}) in regionfile {} will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // rewrite aikar oversized data
|
||||
+
|
||||
+ this.oversizedCount = 0;
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+ int isAikarOversized = hasAikarOversized[location] ? 1 : 0;
|
||||
+
|
||||
+ this.oversizedCount += isAikarOversized;
|
||||
+ this.oversized[location] = (byte)isAikarOversized;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (this.oversizedCount > 0) {
|
||||
+ try {
|
||||
+ this.writeOversizedMeta();
|
||||
+ } catch (Exception ex) {
|
||||
+ LOGGER.error("Failed to write aikar oversized chunk meta, all aikar style oversized chunk data will be lost for regionfile {}", this.path.toAbsolutePath(), ex);
|
||||
+ Files.deleteIfExists(this.getOversizedMetaFile());
|
||||
+ }
|
||||
+ } else {
|
||||
+ Files.deleteIfExists(this.getOversizedMetaFile());
|
||||
+ }
|
||||
+
|
||||
+ this.usedSectors.copyFrom(newSectorAllocations);
|
||||
+
|
||||
+ // before we overwrite the old sectors, print a summary of the chunks that got changed.
|
||||
+
|
||||
+ LOGGER.info("Starting summary of changes for regionfile {}", this.path.toAbsolutePath());
|
||||
+
|
||||
+ for (int chunkX = 0; chunkX < 32; ++chunkX) {
|
||||
+ for (int chunkZ = 0; chunkZ < 32; ++chunkZ) {
|
||||
+ int location = chunkX | (chunkZ << 5);
|
||||
+
|
||||
+ int oldOffset = this.offsets.get(location);
|
||||
+ int newOffset = calculatedOffsets[location];
|
||||
+
|
||||
+ if (oldOffset == newOffset) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ this.offsets.put(location, newOffset); // overwrite incorrect offset
|
||||
+
|
||||
+ if (oldOffset == 0) {
|
||||
+ // found lost data
|
||||
+ LOGGER.info("Found missing data for local chunk ({},{}) in regionfile {}", chunkX, chunkZ, this.path.toAbsolutePath());
|
||||
+ } else if (newOffset == 0) {
|
||||
+ LOGGER.warn("Data for local chunk ({},{}) could not be recovered in regionfile {}, it will be regenerated", chunkX, chunkZ, this.path.toAbsolutePath());
|
||||
+ } else {
|
||||
+ LOGGER.info("Local chunk ({},{}) changed to point to newer data or correct chunk in regionfile {}", chunkX, chunkZ, this.path.toAbsolutePath());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOGGER.info("End of change summary for regionfile {}", this.path.toAbsolutePath());
|
||||
+
|
||||
+ // simply destroy the timestamp header, it's not used
|
||||
+
|
||||
+ for (int i = 0; i < 32 * 32; ++i) {
|
||||
+ this.timestamps.put(i, calculatedOffsets[i] != 0 ? RegionFile.getTimestamp() : 0); // write a valid timestamp for valid chunks, I do not want to find out whatever dumb program actually checks this
|
||||
+ }
|
||||
+
|
||||
+ // write new header
|
||||
+ try {
|
||||
+ this.flush();
|
||||
+ this.file.force(true); // try to ensure it goes through...
|
||||
+ LOGGER.info("Successfully wrote new header to disk for regionfile {}", this.path.toAbsolutePath());
|
||||
+ } catch (IOException ex) {
|
||||
+ LOGGER.error("Failed to write new header to disk for regionfile {}", this.path.toAbsolutePath(), ex);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ final boolean canRecalcHeader; // final forces compile fail on new constructor
|
||||
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
|
||||
+
|
||||
// Paper start - rewrite chunk system
|
||||
@Override
|
||||
public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(final net.minecraft.nbt.CompoundTag data, final ChunkPos pos) throws IOException {
|
||||
@@ -75,6 +432,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
}
|
||||
|
||||
this.externalFileDir = externalFileDir;
|
||||
+ this.canRecalcHeader = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - add can recalc flag
|
||||
this.offsets = this.header.asIntBuffer();
|
||||
this.offsets.limit(1024);
|
||||
this.header.position(4096);
|
||||
@@ -95,11 +453,13 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
|
||||
long size = Files.size(path);
|
||||
|
||||
- for (int i = 0; i < 1024; i++) {
|
||||
+ boolean needsHeaderRecalc = false; // Paper - recalculate header on header corruption
|
||||
+ boolean hasBackedUp = false; // Paper - recalculate header on header corruption
|
||||
+ for (int i = 0; i < 1024; i++) { final int headerLocation = i; // Paper - diff on change, we expect this to be the header location
|
||||
int offset = this.offsets.get(i);
|
||||
if (offset != 0) {
|
||||
- int sectorNumber = getSectorNumber(offset);
|
||||
- int numSectors = getNumSectors(offset);
|
||||
+ int sectorNumber = getSectorNumber(offset); // Paper - diff on change, we expect this to be offset in file in sectors
|
||||
+ int numSectors = getNumSectors(offset); // Paper - diff on change, we expect this to be sector length of region - watch out for reassignments
|
||||
// Spigot start
|
||||
if (numSectors == 255) {
|
||||
// We're maxed out, so we need to read the proper length from the section
|
||||
@@ -110,18 +470,62 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
// Spigot end
|
||||
if (sectorNumber < 2) {
|
||||
LOGGER.warn("Region file {} has invalid sector at index: {}; sector {} overlaps with header", path, i, sectorNumber);
|
||||
- this.offsets.put(i, 0);
|
||||
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else if (numSectors == 0) {
|
||||
LOGGER.warn("Region file {} has an invalid sector at index: {}; size has to be > 0", path, i);
|
||||
- this.offsets.put(i, 0);
|
||||
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else if (sectorNumber * 4096L > size) {
|
||||
LOGGER.warn("Region file {} has an invalid sector at index: {}; sector {} is out of bounds", path, i, sectorNumber);
|
||||
- this.offsets.put(i, 0);
|
||||
+ //this.offsets.put(i, 0); // Paper - we catch this, but need it in the header for the summary change
|
||||
} else {
|
||||
- this.usedSectors.force(sectorNumber, numSectors);
|
||||
+ //this.usedSectors.force(sectorNumber, numSectors); // Paper - move this down so we can check if it fails to allocate
|
||||
+ }
|
||||
+ // Paper start - recalculate header on header corruption
|
||||
+ if (sectorNumber < 2 || numSectors <= 0 || ((long)sectorNumber * 4096L) > size) {
|
||||
+ if (this.canRecalcHeader) {
|
||||
+ LOGGER.error("Detected invalid header for regionfile {}! Recalculating header...", this.path.toAbsolutePath());
|
||||
+ needsHeaderRecalc = true;
|
||||
+ break;
|
||||
+ } else {
|
||||
+ // location = chunkX | (chunkZ << 5);
|
||||
+ LOGGER.error("Detected invalid header for regionfile {}! Cannot recalculate, removing local chunk ({},{}) from header",
|
||||
+ this.path.toAbsolutePath(), headerLocation & 31, headerLocation >>> 5);
|
||||
+ if (!hasBackedUp) {
|
||||
+ hasBackedUp = true;
|
||||
+ this.backupRegionFile();
|
||||
+ }
|
||||
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
|
||||
+ this.offsets.put(headerLocation, 0); // delete the entry from header
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
+ boolean failedToAllocate = !this.usedSectors.tryAllocate(sectorNumber, numSectors);
|
||||
+ if (failedToAllocate) {
|
||||
+ LOGGER.error("Overlapping allocation by local chunk ({},{}) in regionfile {}", headerLocation & 31, headerLocation >>> 5, this.path.toAbsolutePath());
|
||||
}
|
||||
+ if (failedToAllocate & !this.canRecalcHeader) {
|
||||
+ // location = chunkX | (chunkZ << 5);
|
||||
+ LOGGER.error("Detected invalid header for regionfile {}! Cannot recalculate, removing local chunk ({},{}) from header",
|
||||
+ this.path.toAbsolutePath(), headerLocation & 31, headerLocation >>> 5);
|
||||
+ if (!hasBackedUp) {
|
||||
+ hasBackedUp = true;
|
||||
+ this.backupRegionFile();
|
||||
+ }
|
||||
+ this.timestamps.put(headerLocation, 0); // be consistent, delete the timestamp too
|
||||
+ this.offsets.put(headerLocation, 0); // delete the entry from header
|
||||
+ continue;
|
||||
+ }
|
||||
+ needsHeaderRecalc |= failedToAllocate;
|
||||
+ // Paper end - recalculate header on header corruption
|
||||
}
|
||||
}
|
||||
+ // Paper start - recalculate header on header corruption
|
||||
+ // we move the recalc here so comparison to old header is correct when logging to console
|
||||
+ if (needsHeaderRecalc) { // true if header gave us overlapping allocations or had other issues
|
||||
+ LOGGER.error("Recalculating regionfile {}, header gave erroneous offsets & locations", this.path.toAbsolutePath());
|
||||
+ this.recalculateHeader();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,10 +534,34 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
}
|
||||
|
||||
private Path getExternalChunkPath(final ChunkPos pos) {
|
||||
- String externalFileName = "c." + pos.x() + "." + pos.z() + ".mcc";
|
||||
+ String externalFileName = "c." + pos.x() + "." + pos.z() + ".mcc"; // Paper - diff on change
|
||||
return this.externalFileDir.resolve(externalFileName);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static @Nullable ChunkPos getOversizedChunkPair(Path file) {
|
||||
+ String fileName = file.getFileName().toString();
|
||||
+
|
||||
+ if (!fileName.startsWith("c.") || !fileName.endsWith(".mcc")) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ String[] split = fileName.split("\\.");
|
||||
+
|
||||
+ if (split.length != 4) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ int x = Integer.parseInt(split[1]);
|
||||
+ int z = Integer.parseInt(split[2]);
|
||||
+
|
||||
+ return new ChunkPos(x, z);
|
||||
+ } catch (NumberFormatException ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
public synchronized @Nullable DataInputStream getChunkDataInputStream(final ChunkPos pos) throws IOException {
|
||||
int offset = this.getOffset(pos);
|
||||
if (offset == 0) {
|
||||
@@ -155,6 +583,11 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
buffer.flip();
|
||||
if (buffer.remaining() < 5) {
|
||||
LOGGER.error("Chunk {} header is truncated: expected {} but read {}", pos, sectorsLength, buffer.remaining());
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -162,6 +595,11 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
byte versionId = buffer.get();
|
||||
if (length == 0) {
|
||||
LOGGER.warn("Chunk {} is allocated, but stream is missing", pos);
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -169,18 +607,45 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
if (isExternalStreamChunk(versionId)) {
|
||||
if (streamLength != 0) {
|
||||
LOGGER.warn("Chunk has both internal and external streams");
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
}
|
||||
|
||||
- return this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ final DataInputStream ret = this.createExternalChunkInputStream(pos, getExternalChunkVersion(versionId));
|
||||
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ return ret;
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
} else if (streamLength > buffer.remaining()) {
|
||||
LOGGER.error("Chunk {} stream is truncated: expected {} but read {}", pos, streamLength, buffer.remaining());
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else if (streamLength < 0) {
|
||||
LOGGER.error("Declared size {} of chunk {} is negative", length, pos);
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ if (this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
return null;
|
||||
} else {
|
||||
JvmProfiler.INSTANCE.onRegionFileRead(this.info, pos, this.version, streamLength);
|
||||
- return this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
|
||||
+ // Paper start - recalculate header on regionfile corruption
|
||||
+ final DataInputStream ret = this.createChunkInputStream(pos, versionId, createStream(buffer, streamLength));
|
||||
+ if (ret == null && this.canRecalcHeader && this.recalculateHeader()) {
|
||||
+ return this.getChunkDataInputStream(pos);
|
||||
+ }
|
||||
+ return ret;
|
||||
+ // Paper end - recalculate header on regionfile corruption
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,9 +822,14 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
|
||||
}
|
||||
|
||||
private ByteBuffer createExternalStub() {
|
||||
+ // Paper start - add version param
|
||||
+ return this.createExternalStub(this.version);
|
||||
+ }
|
||||
+ private ByteBuffer createExternalStub(RegionFileVersion version) {
|
||||
+ // Paper end - add version param
|
||||
ByteBuffer stub = ByteBuffer.allocate(5);
|
||||
stub.putInt(1);
|
||||
- stub.put((byte)(this.version.getId() | EXTERNAL_STREAM_FLAG));
|
||||
+ stub.put((byte)(version.getId() | EXTERNAL_STREAM_FLAG)); // Paper - add version param
|
||||
stub.flip();
|
||||
return stub;
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 4d3abebdb8b5f952837acb3093ff6e04c44b08e4..6db9314f12dd69ab82d689ab77b7cc6bd53af102 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -27,6 +27,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
private final Path folder;
|
||||
private final boolean sync;
|
||||
|
||||
+ // Paper start - recalculate region file headers
|
||||
+ private final boolean isChunkData;
|
||||
+
|
||||
+ @Nullable
|
||||
+ public static ChunkPos getRegionFileCoordinates(Path file) {
|
||||
+ String fileName = file.getFileName().toString();
|
||||
+ if (!fileName.startsWith("r.") || !fileName.endsWith(".mca")) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ String[] split = fileName.split("\\.");
|
||||
+
|
||||
+ if (split.length != 4) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ int x = Integer.parseInt(split[1]);
|
||||
+ int z = Integer.parseInt(split[2]);
|
||||
+
|
||||
+ return new ChunkPos(x << 5, z << 5);
|
||||
+ } catch (NumberFormatException ex) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
// Paper start - rewrite chunk system
|
||||
private static final int REGION_SHIFT = 5;
|
||||
private static final int MAX_NON_EXISTING_CACHE = 1024 * 4;
|
||||
@@ -144,12 +170,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
|
||||
if (input == null) {
|
||||
return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
||||
- ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.NO_DATA, null, null
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.NO_DATA, null, null, regionFile == null ? 0 : regionFile.getRecalculateCount() // Paper - Attempt to recalculate regionfile header if it is corrupt
|
||||
);
|
||||
}
|
||||
|
||||
final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData ret = new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
||||
- ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.HAS_DATA, input, null
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.HAS_DATA, input, null, regionFile.getRecalculateCount() // Paper - Attempt to recalculate regionfile header if it is corrupt
|
||||
);
|
||||
|
||||
if (!(input instanceof ca.spottedleaf.moonrise.patches.chunk_system.util.stream.ExternalChunkStreamMarker)) {
|
||||
@@ -165,7 +191,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
}
|
||||
|
||||
return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
||||
- ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.SYNC_READ, null, syncRead
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.SYNC_READ, null, syncRead, regionFile.getRecalculateCount() // Paper - Attempt to recalculate regionfile header if it is corrupt
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,7 +201,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
final int chunkX, final int chunkZ, final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData readData
|
||||
) throws IOException {
|
||||
try {
|
||||
- return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(readData.input()); // Paper - Optimize NBT reading/writing
|
||||
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
|
||||
+ final CompoundTag ret = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(readData.input()); // Paper - Optimize NBT reading/writing
|
||||
+ if (!this.isChunkData) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
||||
+ final ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(ret);
|
||||
+ final RegionFile regionFile = this.getRegionFile(pos, true);
|
||||
+
|
||||
+ if (regionFile.getRecalculateCount() != readData.recalculateCount()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (!headerChunkPos.equals(pos)) {
|
||||
+ LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + headerChunkPos + " instead! Attempting regionfile recalculation " + regionFile.getPath().toAbsolutePath());
|
||||
+ if (regionFile.recalculateHeader()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ LOGGER.error(com.mojang.logging.LogUtils.FATAL_MARKER, "Can't recalculate regionfile header?");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
|
||||
} finally {
|
||||
readData.input().close();
|
||||
}
|
||||
@@ -186,6 +237,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
this.folder = folder;
|
||||
this.sync = sync;
|
||||
this.info = info;
|
||||
+ this.isChunkData = info.dfuType()[0] == net.minecraft.util.datafix.DataFixTypes.CHUNK; // Paper - recalculate region file headers
|
||||
}
|
||||
|
||||
private @Nullable RegionFile getRegionFile(final ChunkPos pos, final boolean create) throws IOException {
|
||||
@@ -293,7 +345,25 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
||||
// Paper end
|
||||
|
||||
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
|
||||
- return regionChunkInputStream == null ? null : ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
|
||||
+ // Paper start - recover from corrupt regionfile header
|
||||
+ if (regionChunkInputStream == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final CompoundTag serialisedChunkData = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
|
||||
+ if (this.isChunkData) {
|
||||
+ ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(serialisedChunkData);
|
||||
+ if (!headerChunkPos.equals(pos)) {
|
||||
+ LOGGER.error("Attempting to read chunk data at {} but got chunk data for {} instead! Attempting regionfile recalculation for regionfile {}", pos, headerChunkPos, region.getPath().toAbsolutePath());
|
||||
+ if (region.recalculateHeader()) {
|
||||
+ return this.read(pos);
|
||||
+ }
|
||||
+ LOGGER.error("Can't recalculate regionfile header, regenerating chunk {} for {}", pos, region.getPath().toAbsolutePath());
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ return serialisedChunkData;
|
||||
+ // Paper end - recover from corrupt regionfile header
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
index ea229f63f81c5638ded74c28e1e13d8948f2e5e0..d991fccfc3d2cc51611f802a6d40429024e21d9a 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionFileVersion.java
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
|
||||
|
||||
public class RegionFileVersion {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
- private static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>();
|
||||
+ public static final Int2ObjectMap<RegionFileVersion> VERSIONS = new Int2ObjectOpenHashMap<>(); // Paper - private -> public
|
||||
private static final Object2ObjectMap<String, RegionFileVersion> VERSIONS_BY_NAME = new Object2ObjectOpenHashMap<>();
|
||||
public static final RegionFileVersion VERSION_GZIP = register(
|
||||
new RegionFileVersion(1, "gzip", in -> new FastBufferedInputStream(new GZIPInputStream(in)), out -> new BufferedOutputStream(new GZIPOutputStream(out))) // Paper - Enable for Configurable region compression format
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
|
||||
index 590bd1ca60956e5c5be58481f97401e55f9e1aeb..4e0c9fa1eecfa9f21285b7911e25830bbcbc8d86 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java
|
||||
@@ -3,7 +3,12 @@ package net.minecraft.world.level.chunk.storage;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
-public record RegionStorageInfo(String level, ResourceKey<Level> dimension, String type) {
|
||||
+// Paper start - crazy hack to indicate what type the region file is
|
||||
+public record RegionStorageInfo(String level, ResourceKey<Level> dimension, String type, net.minecraft.util.datafix.DataFixTypes[] dfuType) {
|
||||
+ public RegionStorageInfo(String level, ResourceKey<Level> dimension, String type) {
|
||||
+ this(level, dimension, type, new net.minecraft.util.datafix.DataFixTypes[1]);
|
||||
+ }
|
||||
+ // Paper end - crazy hack to indicate what type the region file is
|
||||
public RegionStorageInfo withTypeSuffix(final String suffix) {
|
||||
return new RegionStorageInfo(this.level, this.dimension, this.type + suffix);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
index 84a92dc868e61969460dddb156af332e6384bcd1..3fef98e8c3c9908c6f05ded33596cbcb18996c3d 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
|
||||
@@ -119,6 +119,18 @@ public record SerializableChunkData(
|
||||
}
|
||||
}
|
||||
// Paper end - guard against serializing mismatching coordinates
|
||||
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
|
||||
+ // TODO: Check on update
|
||||
+ public static long getLastWorldSaveTime(final CompoundTag chunkData) {
|
||||
+ final int dataVersion = NbtUtils.getDataVersion(chunkData);
|
||||
+ if (dataVersion < 2842) { // Level tag is removed after this version
|
||||
+ final CompoundTag levelData = chunkData.getCompoundOrEmpty("Level");
|
||||
+ return levelData.getLongOr("LastUpdate", 0L);
|
||||
+ } else {
|
||||
+ return chunkData.getLongOr("LastUpdate", 0L);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Attempt to recalculate regionfile header if it is corrupt
|
||||
|
||||
// Paper start - Do not let the server load chunks from newer versions
|
||||
private static final int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().dataVersion().version();
|
||||
@@ -528,7 +540,7 @@ public record SerializableChunkData(
|
||||
tag.putInt("xPos", this.chunkPos.x());
|
||||
tag.putInt("yPos", this.minSectionY);
|
||||
tag.putInt("zPos", this.chunkPos.z());
|
||||
- tag.putLong("LastUpdate", this.lastUpdateTime);
|
||||
+ tag.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change
|
||||
tag.putLong("InhabitedTime", this.inhabitedTime);
|
||||
tag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
|
||||
tag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData);
|
||||
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
index cdbe291d2053239e3b8dd0042dfb475306a6b6e5..12ac13394fc38f089be0ad7731eb4ea9487e1d17 100644
|
||||
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
|
||||
@@ -34,6 +34,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
|
||||
public SimpleRegionStorage(
|
||||
final RegionStorageInfo info, final Path folder, final DataFixer fixerUpper, final boolean syncWrites, final DataFixTypes dataFixType
|
||||
) {
|
||||
+ info.dfuType()[0] = dataFixType; // Paper - crazy hack to indicate what type the region file is
|
||||
this.fixerUpper = fixerUpper;
|
||||
this.dataFixType = dataFixType;
|
||||
this.storage = new IOWorker(info, folder, syncWrites).storage; // Paper - rewrite chunk system
|
||||
+1
-1
@@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
|
||||
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index d920a4905d044925c4c4ab980fc238c9c489d5ac..60decc2534c5bc52b1a22e23a416a7660f724cb0 100644
|
||||
index 19c7dffe5f9c45cd99cb9b14cae5ac908cae0afc..1c92e33104cc131269be15333eeee0ca5f0c92b1 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -647,6 +647,7 @@ public class ServerGamePacketListenerImpl
|
||||
+2
-2
@@ -42,7 +42,7 @@ index 0a60283625536515bfcf6c2a09ca0f4771d6b142..42500899812fe3ef18c09f68a85012b7
|
||||
protected ChunkGenerator generator() {
|
||||
return this.worldGenContext.generator();
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 8f2c37a3a534b3b0d4f3ffd2a0d7013a9b454244..f1ca359abcc422e791c81e036b4777643bce53b3 100644
|
||||
index 2dc8b94c434e841b319ef5b77bd725fd66f3c10b..a5540612a4052d9e9d8b1b0ac3c1591777f26caa 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -547,7 +547,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -75,7 +75,7 @@ index 8f2c37a3a534b3b0d4f3ffd2a0d7013a9b454244..f1ca359abcc422e791c81e036b477764
|
||||
profiler.popPush("tickSpawningChunks");
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 60db677d06207b488aad0363af55012b2d76fe8b..eeeb4075a6bbc4e310c2ad685f4efeade702ee3f 100644
|
||||
index 2cf8854636df72c1dccbb73dfe8f642dfdf1ea37..99403edc1eda1e238b66028b7d236e5fceb235a1 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -422,6 +422,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
+2
-2
@@ -37,7 +37,7 @@ index 42500899812fe3ef18c09f68a85012b705c1010d..fdcbf132e2045bc6828028556ca4c354
|
||||
// Paper end - Optional per player mob spawns
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index f1ca359abcc422e791c81e036b4777643bce53b3..ef53d4d17222a882dd3d33575096c864a0a00a06 100644
|
||||
index a5540612a4052d9e9d8b1b0ac3c1591777f26caa..c4cfb53e145d69666ffc27ef4ee0c50b54eb9143 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -552,7 +552,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -60,7 +60,7 @@ index f1ca359abcc422e791c81e036b4777643bce53b3..ef53d4d17222a882dd3d33575096c864
|
||||
spawnCookie = NaturalSpawner.createState(chunkCount, this.level, this::getFullChunk, null, true);
|
||||
} else {
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index eeeb4075a6bbc4e310c2ad685f4efeade702ee3f..1137be30f125cb79f05f3e5c104cdf8cca979ff1 100644
|
||||
index 99403edc1eda1e238b66028b7d236e5fceb235a1..23b3a82ab3e1c6abc85000d9285470cb12300a6b 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -426,6 +426,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
+1
-1
@@ -5,7 +5,7 @@ Subject: [PATCH] DataConverter, Moonrise & co fixes
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 60decc2534c5bc52b1a22e23a416a7660f724cb0..8178922f5ecc7f6709335c7fbeb6dca505b044bb 100644
|
||||
index 1c92e33104cc131269be15333eeee0ca5f0c92b1..6dc9e669a37ddc0a17099bcc4839b42fe237cf99 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -1857,9 +1857,14 @@ public class ServerGamePacketListenerImpl
|
||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index bd5efcf1dee77c204bf29ef4693ac54146e8d901..7517b872794f3bbe556f3abbeb294f426c8da899 100644
|
||||
index b675e9322234761a69e71e9e8b9d41364caf34b6..c3bf41af0273e902f881cc070fcef6dd64abb063 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1799,6 +1799,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -1797,6 +1797,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
level.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
level.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
level.updateLagCompensationTick(); // Paper - lag compensation
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1707
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
--- /dev/null
|
||||
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
|
||||
@@ -1,0 +_,271 @@
|
||||
@@ -1,0 +_,260 @@
|
||||
+package ca.spottedleaf.moonrise.paper;
|
||||
+
|
||||
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
@@ -13,7 +13,6 @@
|
||||
+import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.nbt.NbtOps;
|
||||
+import net.minecraft.server.level.ChunkHolder;
|
||||
+import net.minecraft.server.level.FullChunkStatus;
|
||||
+import net.minecraft.server.level.GenerationChunkHolder;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -89,11 +88,6 @@
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onFullChunkStatusChange(final LevelChunk chunk, final FullChunkStatus from, final FullChunkStatus to) {
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
|
||||
+
|
||||
+ }
|
||||
@@ -168,11 +162,6 @@
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean configFixMC119599(final ServerLevel world) {
|
||||
+ return world.paperConfig().environment.fixMissingTreeSnow;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean configAutoConfigSendDistance() {
|
||||
+ return io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.autoConfigSendDistance;
|
||||
+ }
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@
|
||||
+ itemEntity.setItem(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ itemEntity.setDeltaMovement(org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getVelocity()));
|
||||
+
|
||||
+ if (source.state().is(net.minecraft.world.level.block.Blocks.DISPENSER) && event.getItem().getType() != craftItem.getType()) {
|
||||
+ if (source.state().is(net.minecraft.world.level.block.Blocks.DISPENSER) && !event.getItem().getType().equals(craftItem.getType())) {
|
||||
+ // Chain to handler for new item
|
||||
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(source, eventStack);
|
||||
|
||||
+29
-40
@@ -134,14 +134,14 @@
|
||||
return super.execute(source, dispensed);
|
||||
}
|
||||
|
||||
level.gameEvent(null, GameEvent.FLUID_PICKUP, target);
|
||||
Item targetType = pickup.getItem();
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
level.gameEvent(null, GameEvent.FLUID_PICKUP, target);
|
||||
Item targetType = pickup.getItem();
|
||||
+ pickup = bucket.pickupBlock(null, level, target, blockState); // CraftBukkit - from above
|
||||
return this.consumeWithRemainder(source, dispensed, new ItemStack(targetType));
|
||||
} else {
|
||||
@@ -250,20 +250,20 @@
|
||||
this.setSuccess(true);
|
||||
return dispensed;
|
||||
}
|
||||
@@ -231,6 +_,13 @@
|
||||
@@ -230,6 +_,13 @@
|
||||
Level level = source.level();
|
||||
Direction direction = source.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos target = source.pos().relative(direction);
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
if (level.isEmptyBlock(target) && WitherSkullBlock.canSpawnMob(level, target, dispensed)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
level.setBlockAndUpdate(
|
||||
target, Blocks.WITHER_SKELETON_SKULL.defaultBlockState().setValue(SkullBlock.ROTATION, RotationSegment.convertToSegment(direction))
|
||||
);
|
||||
@@ -242,7 +_,7 @@
|
||||
dispensed.shrink(1);
|
||||
this.setSuccess(true);
|
||||
@@ -273,20 +273,20 @@
|
||||
}
|
||||
|
||||
return dispensed;
|
||||
@@ -256,6 +_,13 @@
|
||||
@@ -255,6 +_,13 @@
|
||||
Level level = source.level();
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
CarvedPumpkinBlock pumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN;
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
if (level.isEmptyBlock(target) && pumpkinBlock.canSpawnGolem(level, target)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
if (!level.isClientSide()) {
|
||||
level.setBlockAndUpdate(target, pumpkinBlock.defaultBlockState());
|
||||
level.gameEvent(null, GameEvent.BLOCK_PLACE, target);
|
||||
@@ -264,7 +_,7 @@
|
||||
dispensed.shrink(1);
|
||||
this.setSuccess(true);
|
||||
@@ -296,30 +296,19 @@
|
||||
}
|
||||
|
||||
return dispensed;
|
||||
@@ -289,11 +_,23 @@
|
||||
@@ -287,6 +_,12 @@
|
||||
ServerLevel level = source.level();
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
BlockState state = level.getBlockState(target);
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
if (state.is(BlockTags.BEEHIVES, s -> s.hasProperty(BeehiveBlock.HONEY_LEVEL) && s.getBlock() instanceof BeehiveBlock)
|
||||
&& state.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
((BeehiveBlock)state.getBlock())
|
||||
.releaseBeesAndResetHoneyLevel(level, state, target, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
|
||||
this.setSuccess(true);
|
||||
return this.takeLiquid(source, dispensed, new ItemStack(Items.HONEY_BOTTLE));
|
||||
} else if (level.getFluidState(target).is(FluidTags.WATER)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, target, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
this.setSuccess(true);
|
||||
return this.takeLiquid(source, dispensed, PotionContents.createItemStack(Items.POTION, Potions.WATER));
|
||||
} else {
|
||||
@@ -312,6 +_,13 @@
|
||||
this.setSuccess(true);
|
||||
if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
|
||||
|
||||
+10
-59
@@ -1,24 +1,19 @@
|
||||
--- a/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
|
||||
@@ -25,21 +_,55 @@
|
||||
@@ -24,16 +_,27 @@
|
||||
this.setSuccess(true);
|
||||
Direction facing = source.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos targetPos = source.pos().relative(facing);
|
||||
BlockState target = level.getBlockState(targetPos);
|
||||
- if (!tryIgniteExplosiveEntities(level, targetPos)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ final @org.jspecify.annotations.Nullable ItemStack[] blockDispenseEventResult = new ItemStack[1];
|
||||
+ if (!tryIgniteExplosiveEntities(level, targetPos, () ->
|
||||
+ (blockDispenseEventResult[0] = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this)) == null)
|
||||
+ ) {
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
BlockState target = level.getBlockState(targetPos);
|
||||
if (!tryIgniteExplosiveEntities(level, targetPos)) {
|
||||
if (BaseFireBlock.canBePlacedAt(level, targetPos, facing)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
+ // CraftBukkit start - Ignition by dispensing flint and steel
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, targetPos, source.pos()).isCancelled()) {
|
||||
level.setBlockAndUpdate(targetPos, BaseFireBlock.getState(level, targetPos));
|
||||
@@ -26,55 +21,11 @@
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
} else if (CampfireBlock.canLight(target) || CandleBlock.canLight(target) || CandleCakeBlock.canLight(target)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
level.setBlockAndUpdate(targetPos, target.setValue(BlockStateProperties.LIT, true));
|
||||
level.gameEvent(null, GameEvent.BLOCK_CHANGE, targetPos);
|
||||
} else if (target.getBlock() instanceof TntBlock) {
|
||||
- if (TntBlock.prime(level, targetPos, null, dispensed)) {
|
||||
+ // Paper start - Call BlockDispenseEvent / TNTPrimeEvent
|
||||
+ final java.util.function.BooleanSupplier tntPrimedEvents = () -> (
|
||||
+ blockDispenseEventResult[0] = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this)) == null
|
||||
+ && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, source.pos());
|
||||
+ if (TntBlock.prime(level, targetPos, null, dispensed, tntPrimedEvents)) {
|
||||
+ // Paper end - Call BlockDispenseEvent / TNTPrimeEvent
|
||||
+ if (TntBlock.prime(level, targetPos, null, dispensed, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, source.pos()))) { // CraftBukkit - TNTPrimeEvent
|
||||
level.removeBlock(targetPos, false);
|
||||
} else {
|
||||
this.setSuccess(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+ // Paper start - Call BlockDispenseEvent - yield back possibly changed result stack from sulfur or tnt dispense events
|
||||
+ if (blockDispenseEventResult[0] != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return blockDispenseEventResult[0];
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent - yield back possibly changed result stack from sulfur or tnt dispense events
|
||||
|
||||
if (this.isSuccess()) {
|
||||
dispensed.hurtAndBreak(1, level, null, item -> {});
|
||||
@@ -48,11 +_,18 @@
|
||||
return dispensed;
|
||||
}
|
||||
|
||||
- private static boolean tryIgniteExplosiveEntities(final ServerLevel level, final BlockPos pos) {
|
||||
+ private static boolean tryIgniteExplosiveEntities(final ServerLevel level, final BlockPos pos, final java.util.function.BooleanSupplier eventSupplier) { // Paper - call BlockDispenseEvent
|
||||
List<SulfurCube> entities = level.getEntitiesOfClass(SulfurCube.class, new AABB(pos), SulfurCube::canExplode);
|
||||
if (entities.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // Paper start - call BlockDispenseEvent
|
||||
+ // If the event was canceled, we return true and stop further event calls.
|
||||
+ // Vanilla would only consider other flint and steel dispense behaviors if no sulfur cube was in the AABB
|
||||
+ // that could explode, which was not the case.
|
||||
+ if (!eventSupplier.getAsBoolean()) return true;
|
||||
+ // Paper end - call BlockDispenseEvent
|
||||
|
||||
entities.getFirst().primeTime(false);
|
||||
return true;
|
||||
+10
-11
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/dispenser/SulfurCubeBlockDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/SulfurCubeBlockDispenseItemBehavior.java
|
||||
@@ -12,14 +_,19 @@
|
||||
@@ -12,12 +_,19 @@
|
||||
|
||||
@Override
|
||||
protected ItemStack execute(final BlockSource source, final ItemStack dispensed) {
|
||||
@@ -11,15 +11,14 @@
|
||||
}
|
||||
|
||||
- public static boolean dispenseBlock(final ServerLevel level, final BlockPos pos, final ItemStack dispensed) {
|
||||
+ public static boolean dispenseBlock(final ServerLevel level, final BlockPos pos, final ItemStack dispensed, final BlockSource source, final @org.jspecify.annotations.Nullable DispenseItemBehavior currentBehavior) { // Paper - Call BlockDispenseEvent
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ public static boolean dispenseBlock(final ServerLevel level, final BlockPos pos, final ItemStack dispensed, final BlockSource source, final @org.jspecify.annotations.Nullable DispenseItemBehavior currentBehavior) {
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, pos, dispensed, currentBehavior);
|
||||
+ if (result != null) { // todo - snapshot - setItem should probably override the equipped item + possible event in the for loop
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
+
|
||||
for (SulfurCube entity : level.getEntitiesOfClass(SulfurCube.class, new AABB(pos))) {
|
||||
- if (entity.equipItem(dispensed)) {
|
||||
+ // Paper start - Call BlockDispenseEvent
|
||||
+ if (entity.canEquipItem(dispensed)) {
|
||||
+ final io.papermc.paper.event.block.PaperBlockDispenseEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEventInspectable(source, pos, dispensed, currentBehavior);
|
||||
+ if (event.result.preventBehaviour()) return true; // Pretend that we succeeded this so we do not call the normal flow.
|
||||
+ entity.equipItem(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
if (entity.equipItem(dispensed)) {
|
||||
dispensed.shrink(1);
|
||||
return true;
|
||||
}
|
||||
@@ -140,7 +140,7 @@
|
||||
|
||||
WorldStem worldStem;
|
||||
try {
|
||||
@@ -169,17 +_,40 @@
|
||||
@@ -169,17 +_,31 @@
|
||||
executor -> WorldLoader.load(
|
||||
worldLoadConfig,
|
||||
context -> {
|
||||
@@ -161,16 +161,8 @@
|
||||
+ }
|
||||
+ // Paper end - migrate startup world
|
||||
LevelDataAndDimensions worldData = LevelStorageSource.getLevelDataAndDimensions(
|
||||
access, levelDataTag, context.dataConfiguration(), datapackDimensions, context.datapackWorldRegistries()
|
||||
+ // Paper start - pass dimension and regenerate missing world gen settings
|
||||
+ , net.minecraft.world.level.Level.OVERWORLD
|
||||
+ , () -> {
|
||||
+ final DedicatedServerProperties properties = settings.getProperties();
|
||||
+ return new WorldGenSettings(
|
||||
+ properties.worldOptions, properties.createDimensions(context.datapackWorldRegistries())
|
||||
+ );
|
||||
+ }
|
||||
+ // Paper end - pass dimension and regenerate missing world gen settings
|
||||
- access, levelDataTag, context.dataConfiguration(), datapackDimensions, context.datapackWorldRegistries()
|
||||
+ access, levelDataTag, context.dataConfiguration(), datapackDimensions, context.datapackWorldRegistries(), net.minecraft.world.level.Level.OVERWORLD // Paper
|
||||
);
|
||||
return new WorldLoader.DataLoadOutput<>(
|
||||
worldData.worldDataAndGenSettings(), worldData.dimensions().dimensionsRegistryAccess()
|
||||
|
||||
@@ -33,11 +33,10 @@
|
||||
private @Nullable ServerStatus status;
|
||||
private ServerStatus.@Nullable Favicon statusIcon;
|
||||
private final RandomSource random = RandomSource.create();
|
||||
@@ -234,13 +_,15 @@
|
||||
@@ -234,13 +_,14 @@
|
||||
private Map<ResourceKey<Level>, ServerLevel> levels = Maps.newLinkedHashMap();
|
||||
private PlayerList playerList;
|
||||
private volatile boolean running = true;
|
||||
+ public volatile boolean hasStarted = false; // Paper
|
||||
+ private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
|
||||
private boolean stopped;
|
||||
private int tickCount;
|
||||
@@ -759,7 +758,7 @@
|
||||
protected void runServer() {
|
||||
try {
|
||||
if (!this.initServer()) {
|
||||
@@ -710,30 +_,74 @@
|
||||
@@ -710,30 +_,73 @@
|
||||
}
|
||||
|
||||
this.nextTickTimeNanos = Util.getNanos();
|
||||
@@ -779,7 +778,6 @@
|
||||
+ // tasks are default scheduled at -1 + delay, and first tick will tick at 1
|
||||
+ final long actualDoneTimeMs = System.currentTimeMillis() - org.bukkit.craftbukkit.Main.BOOT_TIME.toEpochMilli(); // Paper - Improve startup message
|
||||
+ LOGGER.info("Done ({})! For help, type \"help\"", String.format(java.util.Locale.ROOT, "%.3fs", actualDoneTimeMs / 1000.00D)); // Paper - Improve startup message
|
||||
+ this.hasStarted = true;
|
||||
+ org.spigotmc.WatchdogThread.tick();
|
||||
+ // Paper end
|
||||
+ org.spigotmc.WatchdogThread.hasStarted = true; // Paper
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
--- a/net/minecraft/server/commands/RandomCommand.java
|
||||
+++ b/net/minecraft/server/commands/RandomCommand.java
|
||||
@@ -42,7 +_,7 @@
|
||||
.then(drawRandomValueTree("roll", true))
|
||||
.then(
|
||||
Commands.literal("reset")
|
||||
- .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))
|
||||
+ .requires(stack -> stack.getBukkitSender().hasPermission("minecraft.command.random.reset")) // Paper - specific permissions for sub command
|
||||
.then(
|
||||
Commands.literal("*")
|
||||
.executes(c -> resetAllSequences(c.getSource()))
|
||||
@@ -122,7 +_,7 @@
|
||||
.then(
|
||||
Commands.argument("sequence", IdentifierArgument.id())
|
||||
.suggests(RandomCommand::suggestRandomSequence)
|
||||
- .requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))
|
||||
+ .requires(stack -> stack.getBukkitSender().hasPermission("minecraft.command.random.sequence." + (announce ? "roll" : "value"))) // Paper - specific permissions for sub command
|
||||
.executes(
|
||||
c -> randomSample(c.getSource(), RangeArgument.Ints.getRange(c, "range"), IdentifierArgument.getId(c, "sequence"), announce)
|
||||
)
|
||||
+1
-48
@@ -1,26 +1,6 @@
|
||||
--- a/net/minecraft/server/level/PlayerSpawnFinder.java
|
||||
+++ b/net/minecraft/server/level/PlayerSpawnFinder.java
|
||||
@@ -32,6 +_,7 @@
|
||||
private final BlockPos spawnSuggestion;
|
||||
private final int radius;
|
||||
private final int candidateCount;
|
||||
+ private final int candidateArea; // Paper - Sample respawn candidates from the whole square (MC-302446)
|
||||
private final int coprime;
|
||||
private final int offset;
|
||||
private int nextCandidateIndex;
|
||||
@@ -42,13 +_,16 @@
|
||||
this.spawnSuggestion = spawnSuggestion;
|
||||
this.radius = radius;
|
||||
long squareSide = radius * 2L + 1L;
|
||||
- this.candidateCount = (int)Math.min(1024L, squareSide * squareSide);
|
||||
- this.coprime = getCoprime(this.candidateCount);
|
||||
- this.offset = RandomSource.createThreadLocalInstance().nextInt(this.candidateCount);
|
||||
+ // Paper start - Sample respawn candidates from the whole square (MC-302446); only the number of attempts is capped
|
||||
+ this.candidateArea = (int)Math.min(Integer.MAX_VALUE, squareSide * squareSide);
|
||||
+ this.candidateCount = Math.min(ABSOLUTE_MAX_ATTEMPTS, this.candidateArea);
|
||||
+ this.coprime = getCoprime(this.candidateArea);
|
||||
+ this.offset = RandomSource.createThreadLocalInstance().nextInt(this.candidateArea);
|
||||
+ // Paper end - Sample respawn candidates from the whole square (MC-302446)
|
||||
@@ -48,7 +_,7 @@
|
||||
}
|
||||
|
||||
public static CompletableFuture<Vec3> findSpawn(final ServerLevel level, final BlockPos spawnSuggestion) {
|
||||
@@ -29,30 +9,3 @@
|
||||
return CompletableFuture.completedFuture(fixupSpawnHeight(level, spawnSuggestion));
|
||||
}
|
||||
|
||||
@@ -70,7 +_,7 @@
|
||||
private void scheduleNext() {
|
||||
int candidateIndex = this.nextCandidateIndex++;
|
||||
if (candidateIndex < this.candidateCount) {
|
||||
- int value = (this.offset + this.coprime * candidateIndex) % this.candidateCount;
|
||||
+ int value = (int)((this.offset + (long)this.coprime * candidateIndex) % this.candidateArea); // Paper - Sample respawn candidates from the whole square (MC-302446)
|
||||
int deltaX = value % (this.radius * 2 + 1);
|
||||
int deltaZ = value / (this.radius * 2 + 1);
|
||||
int targetX = this.spawnSuggestion.getX() + deltaX - this.radius;
|
||||
@@ -108,7 +_,16 @@
|
||||
}
|
||||
|
||||
private static int getCoprime(final int possibleOrigins) {
|
||||
- return possibleOrigins <= 16 ? possibleOrigins - 1 : 17;
|
||||
+ // Paper start - Sample respawn candidates from the whole square (MC-302446); 17 divides some areas, e.g. 51 * 51
|
||||
+ if (possibleOrigins <= 16) {
|
||||
+ return possibleOrigins - 1;
|
||||
+ }
|
||||
+ int coprime = 17;
|
||||
+ while (com.google.common.math.IntMath.gcd(coprime, possibleOrigins) != 1) {
|
||||
+ coprime++;
|
||||
+ }
|
||||
+ return coprime;
|
||||
+ // Paper end - Sample respawn candidates from the whole square (MC-302446)
|
||||
}
|
||||
|
||||
private void scheduleCandidate(final int candidateX, final int candidateZ, final int candidateIndex, final Supplier<Optional<Vec3>> candidateChecker) {
|
||||
+2
-7
@@ -1222,12 +1222,7 @@
|
||||
this.player
|
||||
.swingAndResetAttackStrength(hand, swingAnimation, success.swingSource() != InteractionResult.SwingSource.PREDICTED);
|
||||
}
|
||||
@@ -1458,24 +_,28 @@
|
||||
} else if (direction == Direction.DOWN && pos.getY() <= minY) {
|
||||
this.player.sendBuildLimitMessage(false, minY);
|
||||
}
|
||||
- } else if (interactionResult instanceof InteractionResult.Success success && success.shouldSwing()) {
|
||||
+ } else if (interactionResult instanceof InteractionResult.Success success && success.shouldSwing() && !this.player.gameMode.interactResult) { // Paper - Call interact event
|
||||
@@ -1462,20 +_,24 @@
|
||||
this.player
|
||||
.swingAndResetAttackStrength(hand, swingAnimation, success.swingSource() != InteractionResult.SwingSource.PREDICTED);
|
||||
}
|
||||
@@ -2114,7 +2109,7 @@
|
||||
+ }
|
||||
+ break;
|
||||
+ case CLONE:
|
||||
+ if (packet.buttonNum() == 0 || packet.buttonNum() == 2) { // Paper - See #11465, workaround for rebound middle clicks in survival, regular middle clicks still don't send a packet
|
||||
+ if (packet.buttonNum() == 2) {
|
||||
+ click = ClickType.MIDDLE;
|
||||
+ if (slotIndex < 0) {
|
||||
+ action = InventoryAction.NOTHING;
|
||||
|
||||
@@ -224,13 +224,12 @@
|
||||
}
|
||||
|
||||
protected abstract void defineSynchedData(SynchedEntityData.Builder entityData);
|
||||
@@ -423,6 +_,49 @@
|
||||
@@ -423,6 +_,48 @@
|
||||
return this.entityData;
|
||||
}
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ public void refreshEntityData(ServerPlayer to) {
|
||||
+ if (this.isRemoved()) return; // Paper - Don't send entity data for removed entities
|
||||
+ List<SynchedEntityData.DataValue<?>> values = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
|
||||
+
|
||||
+ if (to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
|
||||
@@ -605,27 +604,6 @@
|
||||
state.getFluidState().entityInside(this.level(), blockIntersection, this, effectCollector);
|
||||
}
|
||||
|
||||
@@ -1594,7 +_,11 @@
|
||||
|
||||
if (onGround) {
|
||||
if (this.fallDistance > 0.0) {
|
||||
- onState.getBlock().fallOn(this.level(), onState, pos, this, this.fallDistance);
|
||||
+ // Paper start - Add EntityLandEvent
|
||||
+ double distance = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityLandEvent(this.level(), pos, this, this.fallDistance);
|
||||
+ if (distance > 0) {
|
||||
+ onState.getBlock().fallOn(this.level(), onState, pos, this, distance);
|
||||
+ // Paper end - Add EntityLandEvent
|
||||
this.level()
|
||||
.gameEvent(
|
||||
GameEvent.HIT_GROUND,
|
||||
@@ -1603,6 +_,7 @@
|
||||
this, this.mainSupportingBlockPos.<BlockState>map(blockPos -> this.level().getBlockState(blockPos)).orElse(onState)
|
||||
)
|
||||
);
|
||||
+ } // Paper - Add EntityLandEvent
|
||||
}
|
||||
|
||||
this.resetFallDistance();
|
||||
@@ -1818,6 +_,7 @@
|
||||
this.setXRot(Mth.clamp(xRot, -90.0F, 90.0F) % 360.0F);
|
||||
this.yRotO = this.getYRot();
|
||||
|
||||
@@ -208,13 +208,12 @@
|
||||
this.effectsDirty = true;
|
||||
}
|
||||
|
||||
- this.setHealth(input.getFloatOr("Health", this.getMaxHealth()));
|
||||
+ // CraftBukkit start
|
||||
+ input.read("Bukkit.MaxHealth", com.mojang.serialization.Codec.DOUBLE).ifPresent(maxHealth -> {
|
||||
+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(maxHealth);
|
||||
+ });
|
||||
+ // CraftBukkit end
|
||||
+ this.setHealth(input.getFloatOr("Health", this.getMaxHealth()), false); // Paper - Fix MC-17876
|
||||
this.setHealth(input.getFloatOr("Health", this.getMaxHealth()));
|
||||
this.hurtTime = input.getShortOr("HurtTime", (short)0);
|
||||
this.deathTime = input.getShortOr("DeathTime", (short)0);
|
||||
this.setSharedFlag(Entity.FLAG_FALL_FLYING, input.getBooleanOr("FallFlying", false));
|
||||
@@ -424,7 +423,7 @@
|
||||
if (effectInstance != null) {
|
||||
this.onEffectsRemoved(List.of(effectInstance));
|
||||
return true;
|
||||
@@ -1152,18 +_,74 @@
|
||||
@@ -1152,17 +_,62 @@
|
||||
}
|
||||
|
||||
public void heal(final float heal) {
|
||||
@@ -464,8 +463,9 @@
|
||||
return this.entityData.get(DATA_HEALTH_ID);
|
||||
}
|
||||
|
||||
- public void setHealth(final float health) {
|
||||
+ // Paper start - Check for NaN
|
||||
+ public void setHealth(float health, final boolean clamp) { // Paper - Fix MC-17876 - add clamp parameter
|
||||
+ public void setHealth(float health) {
|
||||
+ if (Float.isNaN(health)) { health = getMaxHealth(); if (this.valid) {
|
||||
+ System.err.println("[NAN-HEALTH] " + getScoreboardName() + " had NaN health set");
|
||||
+ } } // Paper end - Check for NaN
|
||||
@@ -475,7 +475,7 @@
|
||||
+ // Squeeze
|
||||
+ if (health < 0.0F) {
|
||||
+ player.setRealHealth(0.0);
|
||||
+ } else if (clamp && health > player.getMaxHealth()) { // Paper - Fix MC-17876
|
||||
+ } else if (health > player.getMaxHealth()) {
|
||||
+ player.setRealHealth(player.getMaxHealth());
|
||||
+ } else {
|
||||
+ player.setRealHealth(health);
|
||||
@@ -485,22 +485,9 @@
|
||||
+ return;
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ // Paper start - Fix MC-17876
|
||||
+ if (clamp) {
|
||||
+ health = Math.clamp(health, 0.0F, this.getMaxHealth());
|
||||
+ } else {
|
||||
+ health = Math.max(health, 0.0F);
|
||||
+ }
|
||||
+
|
||||
+ this.entityData.set(DATA_HEALTH_ID, health);
|
||||
+ }
|
||||
public void setHealth(final float health) {
|
||||
- this.entityData.set(DATA_HEALTH_ID, Mth.clamp(health, 0.0F, this.getMaxHealth()));
|
||||
+ this.setHealth(health, true);
|
||||
+ // Paper end - Fix MC-17876
|
||||
this.entityData.set(DATA_HEALTH_ID, Mth.clamp(health, 0.0F, this.getMaxHealth()));
|
||||
}
|
||||
|
||||
public boolean isDeadOrDying() {
|
||||
@@ -1180,7 +_,7 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-2
@@ -60,8 +60,7 @@
|
||||
+ this.inventory.getItems().stream().filter(item -> !item.isEmpty()).forEach(stack -> this.spawnAtLocation(level, stack)); // Paper - delay removal post event - taken from removeAllItems
|
||||
+ this.postDeathEventTasks.add(this.inventory::clearContent); // Paper - only clear after death event
|
||||
ItemStack itemStack = this.getItemBySlot(EquipmentSlot.MAINHAND);
|
||||
- if (!itemStack.isEmpty() && !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
|
||||
+ if (!itemStack.isEmpty() && !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP) && !this.droppedEquipmentSlots.contains(EquipmentSlot.MAINHAND)) { // Paper
|
||||
if (!itemStack.isEmpty() && !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
|
||||
this.spawnAtLocation(level, itemStack);
|
||||
- this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
|
||||
+ this.postDeathEventTasks.add(() -> this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY)); // Paper
|
||||
|
||||
-30
@@ -128,36 +128,6 @@
|
||||
return itWorked ? InteractionResult.SUCCESS_SERVER : InteractionResult.PASS;
|
||||
} else {
|
||||
return Bucketable.bucketMobPickup(player, hand, this).orElse(super.mobInteract(player, hand));
|
||||
@@ -490,16 +_,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - move out equip item checks
|
||||
+ public boolean canEquipItem(final ItemStack heldItem) {
|
||||
+ if (this.isBaby()) return false;
|
||||
+
|
||||
+ final Item swallowedItem = this.getItemBySlot(EquipmentSlot.BODY).getItem();
|
||||
+ if (heldItem.is(swallowedItem)) return false;
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - move out equip item checks
|
||||
+
|
||||
public boolean equipItem(final ItemStack heldItem) {
|
||||
- if (this.isBaby()) {
|
||||
- return false;
|
||||
- }
|
||||
+ if (!canEquipItem(heldItem)) return false; // Paper - move out equip item checks
|
||||
|
||||
if (this.hasBodyItem()) {
|
||||
- Item swallowedItem = this.getItemBySlot(EquipmentSlot.BODY).getItem();
|
||||
- if (heldItem.is(swallowedItem)) {
|
||||
- return false;
|
||||
- }
|
||||
+ // Paper - move out equip item checks
|
||||
|
||||
Vec3 equipmentSpawnOffset = this.getAttachments().getAverage(EntityAttachment.PASSENGER);
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
@@ -525,7 +_,7 @@
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
for (SulfurCubeArchetype.ContactDamage damage : this.contactDamages) {
|
||||
|
||||
+1
-16
@@ -1,16 +1,5 @@
|
||||
--- a/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java
|
||||
+++ b/net/minecraft/world/entity/projectile/throwableitemprojectile/AbstractThrownPotion.java
|
||||
@@ -47,9 +_,7 @@
|
||||
return 0.05;
|
||||
}
|
||||
|
||||
- @Override
|
||||
- protected void onHitBlock(final BlockHitResult hitResult) {
|
||||
- super.onHitBlock(hitResult);
|
||||
+ protected void extinguishFires(final BlockHitResult hitResult) { // Paper - move after PotionSplashEvent call to prevent putting out fires for cancelled events
|
||||
if (!this.level().isClientSide()) {
|
||||
ItemStack potionItemStack = this.getItem();
|
||||
Direction hitDirection = hitResult.getDirection();
|
||||
@@ -70,14 +_,21 @@
|
||||
@Override
|
||||
protected void onHit(final HitResult hitResult) {
|
||||
@@ -36,7 +25,7 @@
|
||||
if (potion.potion().isPresent() && potion.potion().get().value().hasInstantEffects()) {
|
||||
level.levelEvent(LevelEvent.PARTICLES_INSTANT_POTION_SPLASH, this.blockPosition(), potion.getColor());
|
||||
if (!this.isSilent()) {
|
||||
@@ -89,49 +_,88 @@
|
||||
@@ -89,49 +_,84 @@
|
||||
level.levelEvent(LevelEvent.SOUND_SPELL_POTION_SPLASH, this.blockPosition(), 0);
|
||||
}
|
||||
}
|
||||
@@ -93,10 +82,6 @@
|
||||
+ this, hitResult, affected, rehydrate, extinguish
|
||||
+ );
|
||||
+ if (!event.isCancelled()) {
|
||||
+ if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
+ this.extinguishFires((BlockHitResult) hitResult);
|
||||
+ }
|
||||
+
|
||||
+ for (org.bukkit.entity.LivingEntity affectedEntity : event.getToDamage()) {
|
||||
+ ((org.bukkit.craftbukkit.entity.CraftLivingEntity) affectedEntity).getHandle().hurtServer(level, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F);
|
||||
+ }
|
||||
|
||||
@@ -29,20 +29,3 @@
|
||||
return this.container.stillValid(player);
|
||||
}
|
||||
|
||||
@@ -107,7 +_,15 @@
|
||||
if (this.player instanceof ServerPlayer serverPlayer) {
|
||||
ServerLevel level = serverPlayer.level();
|
||||
CraftingInput craftInput = this.container.asCraftInput();
|
||||
- ItemStack result = CrafterBlock.getPotentialResults(level, craftInput).map(recipe -> recipe.value().assemble(craftInput)).orElse(ItemStack.EMPTY);
|
||||
+ // Paper start - Fire PrepareItemCraftEvent for Crafter preview
|
||||
+ final java.util.Optional<net.minecraft.world.item.crafting.RecipeHolder<net.minecraft.world.item.crafting.CraftingRecipe>> potentialResults = CrafterBlock.getPotentialResults(level, craftInput);
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(
|
||||
+ this.container,
|
||||
+ this.resultContainer,
|
||||
+ potentialResults.map(recipe -> recipe.value().assemble(craftInput)).orElse(ItemStack.EMPTY),
|
||||
+ this.getBukkitView(),
|
||||
+ potentialResults.map(net.minecraft.world.item.crafting.RecipeHolder::value).orElse(null) instanceof net.minecraft.world.item.crafting.RepairItemRecipe);
|
||||
+ // Paper end - Fire PrepareItemCraftEvent for Crafter preview
|
||||
this.resultContainer.setItem(0, result);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class BucketItem extends Item implements DispensibleContainerItem {
|
||||
+ static @Nullable ItemStack itemLeftInHandAfterPlayerBucketEmptyEvent = null; // Paper - Fix PlayerBucketEmptyEvent result itemstack
|
||||
+ private static @Nullable ItemStack itemLeftInHandAfterPlayerBucketEmptyEvent = null; // Paper - Fix PlayerBucketEmptyEvent result itemstack
|
||||
protected final Fluid content;
|
||||
|
||||
public BucketItem(final Fluid content, final Item.Properties properties) {
|
||||
@@ -65,7 +65,7 @@
|
||||
+ return this.emptyContents(user, level, pos, hitResult, null, null, null, InteractionHand.MAIN_HAND);
|
||||
+ }
|
||||
+
|
||||
+ public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult, final @Nullable Direction direction, final @Nullable BlockPos clicked, final @Nullable ItemStack itemStack, final InteractionHand hand) {
|
||||
+ public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult, final Direction direction, final BlockPos clicked, final ItemStack itemStack, final InteractionHand hand) {
|
||||
+ // CraftBukkit end
|
||||
if (!(this.content instanceof FlowingFluid flowingFluid)) {
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/world/item/MobBucketItem.java
|
||||
+++ b/net/minecraft/world/item/MobBucketItem.java
|
||||
@@ -52,18 +_,28 @@
|
||||
@@ -52,18 +_,18 @@
|
||||
}
|
||||
|
||||
if (mob != null) {
|
||||
@@ -14,16 +14,6 @@
|
||||
- public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult) {
|
||||
+ public boolean emptyContents(final @Nullable LivingEntity user, final Level level, final BlockPos pos, final @Nullable BlockHitResult hitResult, final net.minecraft.core.Direction direction, final BlockPos clicked, final ItemStack itemStack, final net.minecraft.world.InteractionHand hand) { // Paper
|
||||
if (this.content == Fluids.EMPTY) {
|
||||
+ // Paper start - call PlayerBucketEmptyEvent for mobs in buckets without fluids
|
||||
+ if (user instanceof net.minecraft.world.entity.player.Player player) {
|
||||
+ org.bukkit.event.player.PlayerBucketEmptyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent(level, player, pos, clicked, direction, itemStack, hand);
|
||||
+ if (event.isCancelled()) {
|
||||
+ player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541
|
||||
+ return false;
|
||||
+ }
|
||||
+ BucketItem.itemLeftInHandAfterPlayerBucketEmptyEvent = event.getItemStack() != null ? event.getItemStack().equals(org.bukkit.inventory.ItemStack.of(org.bukkit.Material.BUCKET)) ? null : org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; // Paper - Fix PlayerBucketEmptyEvent result itemstack
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.playEmptySound(user, level, pos);
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -70,18 +70,16 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -189,8 +_,9 @@
|
||||
@@ -189,7 +_,8 @@
|
||||
|
||||
@Override
|
||||
public void sendSystemMessage(final Component message) {
|
||||
- if (!this.closed) {
|
||||
- BaseCommandBlock.this.lastOutput = Component.literal("[" + TIME_FORMAT.format(ZonedDateTime.now()) + "] ").append(message);
|
||||
+ if (this.trackOutput && !this.closed) { // Paper - add back source when output disabled
|
||||
+ org.spigotmc.AsyncCatcher.catchOp("sendSystemMessage to a command block"); // Paper - Don't broadcast messages to command blocks
|
||||
+ BaseCommandBlock.this.lastOutput = Component.literal("[" + this.level.paperConfig().commandBlocks.timeFormatPattern.or(TIME_FORMAT).format(ZonedDateTime.now()) + "] ").append(message); // Paper - per-world configurable time format
|
||||
BaseCommandBlock.this.lastOutput = Component.literal("[" + TIME_FORMAT.format(ZonedDateTime.now()) + "] ").append(message);
|
||||
BaseCommandBlock.this.onUpdated(this.level);
|
||||
}
|
||||
}
|
||||
@@ -199,5 +_,12 @@
|
||||
public void close() throws Exception {
|
||||
this.closed = true;
|
||||
|
||||
+2
-4
@@ -1,15 +1,13 @@
|
||||
--- a/net/minecraft/world/level/block/BaseFireBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BaseFireBlock.java
|
||||
@@ -133,12 +_,14 @@
|
||||
@@ -133,12 +_,13 @@
|
||||
final InsideBlockEffectApplier effectApplier,
|
||||
final boolean isPrecise
|
||||
) {
|
||||
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
|
||||
+ final BlockPos savedPos = pos.immutable(); // Paper - track fire contact
|
||||
effectApplier.apply(InsideBlockEffectType.CLEAR_FREEZE);
|
||||
effectApplier.apply(InsideBlockEffectType.FIRE_IGNITE);
|
||||
- effectApplier.runAfter(InsideBlockEffectType.FIRE_IGNITE, e -> e.hurt(e.level().damageSources().inFire(), this.fireDamage));
|
||||
+ effectApplier.runAfter(InsideBlockEffectType.FIRE_IGNITE, e -> e.hurt(e.level().damageSources().inFire().eventBlockDamager(level, savedPos), this.fireDamage)); // Paper
|
||||
effectApplier.runAfter(InsideBlockEffectType.FIRE_IGNITE, e -> e.hurt(e.level().damageSources().inFire(), this.fireDamage));
|
||||
}
|
||||
|
||||
- public static void fireIgnite(final Entity entity) {
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
--- a/net/minecraft/world/level/block/entity/PotentSulfurBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/PotentSulfurBlockEntity.java
|
||||
@@ -127,7 +_,19 @@
|
||||
&& !entityToBeLaunched.isPassenger()
|
||||
&& !entityToBeLaunched.is(EntityTypeTags.NOT_AFFECTED_BY_GEYSERS)
|
||||
&& entityVelocity.y < 0.3F + waterBlocks * 0.1) {
|
||||
- entityToBeLaunched.addDeltaMovement(new Vec3(0.0, 0.2F, 0.0));
|
||||
+ // Paper start - add EntityPotentSulfurLaunchEvent
|
||||
+ final Vec3 launchVelocity = new Vec3(0.0, GEYSER_LAUNCH_FORCE, 0.0);
|
||||
+ if (io.papermc.paper.event.entity.EntityPotentSulfurLaunchEvent.getHandlerList().hasListeners()) {
|
||||
+ io.papermc.paper.event.entity.EntityPotentSulfurLaunchEvent event = new io.papermc.paper.event.entity.EntityPotentSulfurLaunchEvent(
|
||||
+ entityToBeLaunched.getBukkitEntity(),
|
||||
+ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos),
|
||||
+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(sourceBlock, level.getWorld()),
|
||||
+ org.bukkit.craftbukkit.util.CraftVector.toBukkit(launchVelocity)
|
||||
+ );
|
||||
+ event.callEvent();
|
||||
+ }
|
||||
+ entityToBeLaunched.addDeltaMovement(launchVelocity);
|
||||
+ // Paper end - add EntityPotentSulfurLaunchEvent
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
-2
@@ -16,14 +16,64 @@
|
||||
Feature feature = featureHolder.value();
|
||||
removeSapling(level, pos);
|
||||
if (feature.place(level, generator, random, pos)) {
|
||||
@@ -229,4 +_,10 @@
|
||||
@@ -229,4 +_,60 @@
|
||||
|
||||
private record TwoByTwoSaplingPos(int offsetX, int offsetZ, List<Pair<BlockState, BlockPos>> groundLevelSurroundingBlocks) {
|
||||
}
|
||||
+
|
||||
+ // CraftBukkit start
|
||||
+ private void setTreeType(Holder<Feature> feature) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.craftbukkit.CraftTreeType.fromFeature(feature.unwrapKey().orElseThrow());
|
||||
+ if (feature.is(TreeFeatures.OAK) || feature.is(TreeFeatures.OAK_BEES_005)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TREE;
|
||||
+ } else if (feature.is(TreeFeatures.HUGE_RED_MUSHROOM)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.RED_MUSHROOM;
|
||||
+ } else if (feature.is(TreeFeatures.HUGE_BROWN_MUSHROOM)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BROWN_MUSHROOM;
|
||||
+ } else if (feature.is(TreeFeatures.JUNGLE_TREE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.COCOA_TREE;
|
||||
+ } else if (feature.is(TreeFeatures.JUNGLE_TREE_NO_VINE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SMALL_JUNGLE;
|
||||
+ } else if (feature.is(TreeFeatures.PINE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_REDWOOD;
|
||||
+ } else if (feature.is(TreeFeatures.SPRUCE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.REDWOOD;
|
||||
+ } else if (feature.is(TreeFeatures.ACACIA)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.ACACIA;
|
||||
+ } else if (feature.is(TreeFeatures.BIRCH) || feature.is(TreeFeatures.BIRCH_BEES_005)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIRCH;
|
||||
+ } else if (feature.is(TreeFeatures.SUPER_BIRCH_BEES_0002)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_BIRCH;
|
||||
+ } else if (feature.is(TreeFeatures.SWAMP_OAK)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.SWAMP;
|
||||
+ } else if (feature.is(TreeFeatures.FANCY_OAK) || feature.is(TreeFeatures.FANCY_OAK_BEES_005)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.BIG_TREE;
|
||||
+ } else if (feature.is(TreeFeatures.JUNGLE_BUSH)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE_BUSH;
|
||||
+ } else if (feature.is(TreeFeatures.DARK_OAK)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.DARK_OAK;
|
||||
+ } else if (feature.is(TreeFeatures.MEGA_SPRUCE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_REDWOOD;
|
||||
+ } else if (feature.is(TreeFeatures.MEGA_PINE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MEGA_PINE;
|
||||
+ } else if (feature.is(TreeFeatures.MEGA_JUNGLE_TREE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.JUNGLE;
|
||||
+ } else if (feature.is(TreeFeatures.AZALEA_TREE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.AZALEA;
|
||||
+ } else if (feature.is(TreeFeatures.MANGROVE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.MANGROVE;
|
||||
+ } else if (feature.is(TreeFeatures.TALL_MANGROVE)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.TALL_MANGROVE;
|
||||
+ } else if (feature.is(TreeFeatures.CHERRY) || feature.is(TreeFeatures.CHERRY_BEES_005)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.CHERRY;
|
||||
+ } else if (feature.is(TreeFeatures.PALE_OAK) || feature.is(TreeFeatures.PALE_OAK_BONEMEAL)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK;
|
||||
+ } else if (feature.is(TreeFeatures.PALE_OAK_CREAKING)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.PALE_OAK_CREAKING;
|
||||
+ } else if (feature.is(TreeFeatures.RED_POPLAR) || feature.is(TreeFeatures.ORANGE_POPLAR) || feature.is(TreeFeatures.YELLOW_POPLAR)) {
|
||||
+ net.minecraft.world.level.block.SaplingBlock.treeType = org.bukkit.TreeType.POPLAR;
|
||||
+ } else {
|
||||
+ throw new IllegalArgumentException("Unknown tree generator " + feature);
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
}
|
||||
+4
-4
@@ -8,8 +8,8 @@
|
||||
+ if (numSectors == 255) {
|
||||
+ // We're maxed out, so we need to read the proper length from the section
|
||||
+ ByteBuffer realLen = ByteBuffer.allocate(4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096L);
|
||||
+ numSectors = sizeToSectors(realLen.getInt(0) + 4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096);
|
||||
+ numSectors = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
if (sectorNumber < 2) {
|
||||
@@ -22,8 +22,8 @@
|
||||
+ // Spigot start
|
||||
+ if (numSectors == 255) {
|
||||
+ ByteBuffer realLen = ByteBuffer.allocate(4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096L);
|
||||
+ numSectors = sizeToSectors(realLen.getInt(0) + 4);
|
||||
+ this.file.read(realLen, sectorNumber * 4096);
|
||||
+ numSectors = (realLen.getInt(0) + 4) / 4096 + 1;
|
||||
+ }
|
||||
+ // Spigot end
|
||||
int sectorsLength = numSectors * 4096;
|
||||
|
||||
+2
-41
@@ -1,32 +1,11 @@
|
||||
--- a/net/minecraft/world/level/storage/LevelStorageSource.java
|
||||
+++ b/net/minecraft/world/level/storage/LevelStorageSource.java
|
||||
@@ -145,21 +_,48 @@
|
||||
@@ -145,13 +_,14 @@
|
||||
final WorldDataConfiguration dataConfiguration,
|
||||
final Registry<LevelStem> datapackDimensions,
|
||||
final HolderLookup.Provider registryAccess
|
||||
- ) {
|
||||
+ , final ResourceKey<Level> dimension // Paper - pass dimension
|
||||
+ ) {
|
||||
+ // Paper start - Regenerate missing overworld world gen settings
|
||||
+ return getLevelDataAndDimensions(worldAccess, levelDataTag, dataConfiguration, datapackDimensions, registryAccess, dimension, () -> {
|
||||
+ // Preserve dimension settings if we got them from the datapack
|
||||
+ WorldDimensions defaultDimensions = datapackDimensions.containsKey(LevelStem.OVERWORLD)
|
||||
+ ? new WorldDimensions(datapackDimensions)
|
||||
+ : net.minecraft.world.level.levelgen.presets.WorldPresets.createNormalWorldDimensions(registryAccess);
|
||||
+ return new WorldGenSettings(WorldOptions.defaultWithRandomSeed(), defaultDimensions);
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ public static LevelDataAndDimensions getLevelDataAndDimensions(
|
||||
+ final LevelStorageSource.LevelStorageAccess worldAccess,
|
||||
+ final Dynamic<?> levelDataTag,
|
||||
+ final WorldDataConfiguration dataConfiguration,
|
||||
+ final Registry<LevelStem> datapackDimensions,
|
||||
+ final HolderLookup.Provider registryAccess,
|
||||
+ final ResourceKey<Level> dimension,
|
||||
+ final java.util.function.Supplier<WorldGenSettings> worldGenSettingsFactory
|
||||
+ ) {
|
||||
+ // Paper end - Regenerate missing overworld world gen settings
|
||||
) {
|
||||
if (DataFixers.getFileFixer().requiresFileFixing(NbtUtils.getDataVersion(levelDataTag))) {
|
||||
throw new IllegalStateException("Cannot get level data without file fixing first");
|
||||
}
|
||||
@@ -37,24 +16,6 @@
|
||||
.mapOrElse(
|
||||
Function.identity(),
|
||||
error -> {
|
||||
- LOGGER.error(
|
||||
- "Unable to read or access the world gen settings file! Falling back to the default settings with a random world seed. {}",
|
||||
- error.message()
|
||||
+ // Paper start - Regenerate missing overworld world gen settings
|
||||
+ Path dataLocation = WorldGenSettings.TYPE.id().withSuffix(".dat").resolveAgainst(worldAccess.getDimensionPath(dimension).resolve(LevelResource.DATA.id()));
|
||||
+ if (!Files.notExists(dataLocation, java.nio.file.LinkOption.NOFOLLOW_LINKS)) {
|
||||
+ throw new IllegalStateException("Unable to read or access the world gen settings file " + dataLocation + ". " + error.message());
|
||||
+ }
|
||||
+ LOGGER.warn(
|
||||
+ "World gen settings file is missing! Regenerating settings from server.properties: {}",
|
||||
+ dataLocation
|
||||
);
|
||||
- return new WorldGenSettings(WorldOptions.defaultWithRandomSeed(), new WorldDimensions(datapackDimensions));
|
||||
+ return worldGenSettingsFactory.get();
|
||||
+ // Paper end - Regenerate missing overworld world gen settings
|
||||
}
|
||||
);
|
||||
LevelSettings settings = LevelSettings.parse(dataTag, dataConfiguration);
|
||||
@@ -170,9 +_,9 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
--- a/net/minecraft/world/scores/Scoreboard.java
|
||||
+++ b/net/minecraft/world/scores/Scoreboard.java
|
||||
@@ -267,6 +_,7 @@
|
||||
}
|
||||
|
||||
this.onTeamRemoved(team);
|
||||
+ team.getPlayers().clear(); // Paper - Clear scoreboard team entries on unregister
|
||||
}
|
||||
|
||||
public boolean addPlayerToTeam(final String player, final PlayerTeam team) {
|
||||
@@ -373,7 +_,7 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.GenerationChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -54,8 +53,6 @@ public interface PlatformHooks extends ChunkSystemHooks {
|
||||
|
||||
public void chunkUnloadFromWorld(final LevelChunk chunk);
|
||||
|
||||
public void onFullChunkStatusChange(final LevelChunk chunk, final FullChunkStatus from, final FullChunkStatus to);
|
||||
|
||||
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
|
||||
|
||||
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
|
||||
@@ -73,8 +70,6 @@ public interface PlatformHooks extends ChunkSystemHooks {
|
||||
|
||||
public boolean configFixMC224294();
|
||||
|
||||
public boolean configFixMC119599(final ServerLevel world);
|
||||
|
||||
public boolean configAutoConfigSendDistance();
|
||||
|
||||
public double configPlayerMaxLoadRate();
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
import oshi.SystemInfo;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class FMA {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
static {
|
||||
final Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
FMAInit.init();
|
||||
}
|
||||
};
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
t.setName(PlatformHooks.get().getBrand() + " FMA initialiser");
|
||||
t.setDaemon(true);
|
||||
t.setUncaughtExceptionHandler((final Thread thread, final Throwable thr) -> {
|
||||
LOGGER.error("Uncaught exception in thread '" + thread.getName() + "': ", thr);
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
public static void init() {}
|
||||
|
||||
public static boolean isFMASupported() {
|
||||
return FMAInit.FMA_SUPPORTED;
|
||||
}
|
||||
|
||||
private static final class FMAInit {
|
||||
|
||||
public static final boolean FMA_SUPPORTED = detectFma();
|
||||
|
||||
private static void init() {}
|
||||
|
||||
private static boolean detectFma() {
|
||||
final String arch = System.getProperty("os.arch", "").toLowerCase(Locale.ROOT);
|
||||
|
||||
final boolean is64 = arch.contains("64");
|
||||
final boolean arm = arch.contains("aarch64") || arch.contains("arm64");
|
||||
if (arm) {
|
||||
// FMA is ARMv8 baseline, Apple Silicon included
|
||||
LOGGER.info("FMA supported: " + true + " for all ARMv8 platforms");
|
||||
return true;
|
||||
}
|
||||
if (!is64) {
|
||||
LOGGER.info("FMA supported: " + false + " for all non 64-bit platforms");
|
||||
return false;
|
||||
}
|
||||
if (System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("linux")) {
|
||||
try {
|
||||
for (final String line : new SystemInfo().getHardware().getProcessor().getFeatureFlags()) {
|
||||
if (line.toLowerCase(Locale.ROOT).matches("(?s).*\\bfma\\b.*")) {
|
||||
LOGGER.info("FMA supported: " + true + " for Linux platform with feature flag: " + line);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("FMA supported: " + false + " for Linux platform without any matching feature flag");
|
||||
return false;
|
||||
} catch (final Throwable thr) {
|
||||
LOGGER.debug("Error while retrieving platform flags: ", thr);
|
||||
LOGGER.info("FMA supported: " + false + " for Linux platform: unable to retrieve feature flags");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Windows, macOS: measure the actual thing
|
||||
final boolean fma = testForFMA();
|
||||
LOGGER.info("FMA supported: " + fma + " as determined by performance comparison test");
|
||||
return fma;
|
||||
}
|
||||
|
||||
private static boolean testForFMA() {
|
||||
// warm into C2
|
||||
for (int i = 0; i < 3; i++) {
|
||||
timeFma(Sink.X);
|
||||
timeMul(Sink.Y);
|
||||
}
|
||||
final long fma = Math.min(
|
||||
timeFma(Sink.X),
|
||||
Math.min(
|
||||
timeFma(Sink.X), timeFma(Sink.X)
|
||||
)
|
||||
);
|
||||
final long mul = Math.min(
|
||||
timeMul(Sink.Y),
|
||||
Math.min(
|
||||
timeMul(Sink.Y), timeMul(Sink.Y)
|
||||
)
|
||||
);
|
||||
// intrinsic fma ~= mul speed; software is 10x+
|
||||
return fma < mul * 3;
|
||||
}
|
||||
|
||||
private static final double A = 1.000001;
|
||||
private static final double B = 0.999999;
|
||||
private static final int LOOPS = 200_000;
|
||||
|
||||
private static long timeFma(final double[] s) {
|
||||
double x = s[0];
|
||||
long t = System.nanoTime();
|
||||
for (int i = 0; i < LOOPS; i++) {
|
||||
x = Math.fma(A, B, x);
|
||||
}
|
||||
s[0] = x;
|
||||
return System.nanoTime() - t;
|
||||
}
|
||||
|
||||
private static long timeMul(final double[] s) {
|
||||
double y = s[0];
|
||||
long t = System.nanoTime();
|
||||
for (int i = 0; i < LOOPS; i++) {
|
||||
y = y * A + B;
|
||||
}
|
||||
s[0] = y;
|
||||
return System.nanoTime() - t;
|
||||
}
|
||||
|
||||
static final class Sink {
|
||||
static final double[] X = { 0.5 };
|
||||
static final double[] Y = { 0.5 };
|
||||
}
|
||||
}
|
||||
|
||||
private FMA() {}
|
||||
}
|
||||
+1
-2
@@ -9,8 +9,7 @@ public final class MoonriseConstants {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
// note: only allows increasing the max view distance, as lower values are smaller than the chunk generation radius
|
||||
public static final int MAX_VIEW_DISTANCE = Math.max(32, Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32));
|
||||
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||
public static final boolean NUMA_ENABLE;
|
||||
static {
|
||||
final boolean numaScheduling = Boolean.getBoolean(PlatformHooks.get().getBrand() + ".NumaScheduling");
|
||||
|
||||
@@ -4,11 +4,7 @@ import io.papermc.paper.configuration.GlobalConfiguration;
|
||||
import io.papermc.paper.console.BrigadierCompletionMatcher;
|
||||
import io.papermc.paper.console.BrigadierConsoleParser;
|
||||
import io.papermc.paper.util.PaperCacheDir;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.util.StringUtil;
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
import org.bukkit.craftbukkit.command.ConsoleCommandCompleter;
|
||||
import org.jline.reader.LineReader;
|
||||
@@ -16,8 +12,6 @@ import org.jline.reader.LineReaderBuilder;
|
||||
|
||||
public final class PaperConsole extends SimpleTerminalConsole {
|
||||
|
||||
private static final ComponentLogger LOGGER = ComponentLogger.logger(PaperConsole.class);
|
||||
|
||||
private final DedicatedServer server;
|
||||
|
||||
public PaperConsole(DedicatedServer server) {
|
||||
@@ -49,27 +43,7 @@ public final class PaperConsole extends SimpleTerminalConsole {
|
||||
|
||||
@Override
|
||||
protected void runCommand(String command) {
|
||||
// terminals interpret pressing [enter] and pasting a multi-line string differently,
|
||||
// the latter makes the line reader read it as a single line - and we don't want that
|
||||
// https://github.com/PaperMC/Paper/issues/13006
|
||||
command.lines()
|
||||
.filter(line -> !line.isEmpty())
|
||||
.forEach(line -> {
|
||||
for (char character : line.toCharArray()) {
|
||||
if (!StringUtil.isAllowedChatCharacter(character)) {
|
||||
LOGGER.info(
|
||||
Component.text()
|
||||
.append(Component.text("Illegal console input character: 0x"))
|
||||
.append(Component.text(Integer.toHexString(character)))
|
||||
.color(NamedTextColor.RED)
|
||||
.build()
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.server.handleConsoleInput(line, this.server.createCommandSourceStack());
|
||||
});
|
||||
this.server.handleConsoleInput(command, this.server.createCommandSourceStack());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-1
@@ -3,14 +3,17 @@ package com.destroystokyo.paper.loottable;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import io.papermc.paper.configuration.WorldConfiguration;
|
||||
import io.papermc.paper.configuration.type.time.DurationOrDisabled;
|
||||
import io.papermc.paper.configuration.type.DurationOrDisabled;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.world.RandomizableContainer;
|
||||
import net.minecraft.world.entity.vehicle.ContainerEntity;
|
||||
import net.minecraft.world.level.storage.ValueInput;
|
||||
|
||||
@@ -12,6 +12,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.spigotmc.RestartCommand;
|
||||
import org.spigotmc.TicksPerSecondCommand;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class PaperCommands {
|
||||
@@ -23,6 +25,8 @@ public final class PaperCommands {
|
||||
|
||||
public static void registerCommands(final MinecraftServer server) {
|
||||
COMMANDS.put("paper", new PaperCommand("paper"));
|
||||
COMMANDS.put("tps", new TicksPerSecondCommand("tps")); // from spigot
|
||||
COMMANDS.put("restart", new RestartCommand("restart")); // from spigot
|
||||
|
||||
COMMANDS.forEach((s, command) -> {
|
||||
server.server.getCommandMap().register(s, "Paper", command);
|
||||
|
||||
-20
@@ -2,8 +2,6 @@ package io.papermc.paper.command.brigadier;
|
||||
|
||||
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
@@ -51,24 +49,6 @@ public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBriga
|
||||
return this.getHandle().withEntity(((CraftEntity) executor).getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendReply(final ComponentLike message) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.getHandle().sendSystemMessage(PaperAdventure.asVanilla(message.asComponent()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendSuccess(final ComponentLike message, final boolean allowInformingAdmins) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.getHandle().sendSuccess(() -> PaperAdventure.asVanilla(message.asComponent()), allowInformingAdmins);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendFailure(final ComponentLike message) {
|
||||
Preconditions.checkArgument(message != null, "message cannot be null.");
|
||||
this.getHandle().sendFailure(PaperAdventure.asVanilla(message.asComponent()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Player getPlayerOrThrow() throws CommandSyntaxException {
|
||||
return this.getHandle().getPlayerOrException().getBukkitEntity();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.papermc.paper.command.subcommands;
|
||||
|
||||
import io.papermc.paper.command.PaperSubcommand;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
@@ -24,9 +23,9 @@ public final class ReloadCommand implements PaperSubcommand {
|
||||
Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED));
|
||||
Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED));
|
||||
|
||||
MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
|
||||
server.paperConfigurations.reloadConfigs(server);
|
||||
server.server.reloadCount++;
|
||||
final CraftServer server = (CraftServer) sender.getServer();
|
||||
server.reloadPaperConfiguration();
|
||||
server.reloadCount++;
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ package io.papermc.paper.configuration;
|
||||
|
||||
public final class Configuration {
|
||||
public static final String VERSION_FIELD = "_version";
|
||||
@Deprecated
|
||||
public static final String LEGACY_CONFIG_VERSION_FIELD = "config-version";
|
||||
|
||||
@Deprecated
|
||||
public static final int FINAL_LEGACY_VERSION = 27;
|
||||
public static final int FINAL_LEGACY_SPIGOT_VERSION = 13;
|
||||
|
||||
private Configuration() {
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package io.papermc.paper.configuration;
|
||||
|
||||
import io.papermc.paper.configuration.yaml.BlankLineStyle;
|
||||
import io.papermc.paper.configuration.yaml.NodeStyle;
|
||||
import io.papermc.paper.configuration.yaml.YamlConfigurationLoader;
|
||||
import java.nio.file.Path;
|
||||
import org.spongepowered.configurate.loader.HeaderMode;
|
||||
import org.spongepowered.configurate.util.MapFactories;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
public final class ConfigurationLoaders {
|
||||
private ConfigurationLoaders() {
|
||||
@@ -15,6 +16,8 @@ public final class ConfigurationLoaders {
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.headerMode(HeaderMode.PRESET)
|
||||
.commentsEnabled(true)
|
||||
.blankLineStyle(BlankLineStyle.AFTER_NESTED)
|
||||
.defaultOptions(options -> options.mapFactory(MapFactories.sortedNatural()));
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user