mirror of
https://github.com/PaperMC/Paper.git
synced 2026-11-04 08:52:01 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
806b0db07c | ||
|
|
e13cf8870f | ||
|
|
cb1e4cc603 | ||
|
|
2fdccd0ddf | ||
|
|
755ecce274 |
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"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release 26.3
|
||||
name: Release 26.4
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -11,7 +11,7 @@ permissions:
|
||||
|
||||
concurrency:
|
||||
cancel-in-progress: false
|
||||
group: release-26.3
|
||||
group: release-26.4
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -1,4 +1,4 @@
|
||||
Paper [](https://papermc.io/downloads/paper)
|
||||
[](https://github.com/PaperMC/Paper/actions)
|
||||
[](https://discord.gg/papermc)
|
||||
@@ -39,7 +39,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:26.3.build.+")
|
||||
compileOnly("io.papermc.paper:paper-api:26.4.build.+")
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -59,7 +59,7 @@ java {
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>[26.3.build,)</version>
|
||||
<version>[26.4.build,)</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
+2
-4
@@ -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
|
||||
@@ -633,14 +630,15 @@ public net.minecraft.world.level.dimension.end.EnderDragonFight spawnNewGateway(
|
||||
public net.minecraft.world.level.entity.PersistentEntitySectionManager ensureChunkQueuedForLoad(J)V
|
||||
public net.minecraft.world.level.entity.PersistentEntitySectionManager permanentStorage
|
||||
public net.minecraft.world.level.gamerules.GameRules rules
|
||||
public net.minecraft.world.level.levelgen.material.MaterialRuleContext context
|
||||
public net.minecraft.world.level.levelgen.structure.StructurePiece SHAPE_CHECK_BLOCKS
|
||||
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement exclusionZone
|
||||
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement frequency
|
||||
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement frequencyReductionMethod
|
||||
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement locateOffset
|
||||
public net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement salt
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate <init>()V
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate entityInfoList
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate fillFromWorld(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Vec3i;ZLjava/util/List;)V
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate palettes
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager resourceManagerSource
|
||||
public net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager structureRepository
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
group=io.papermc.paper
|
||||
mcVersion=26.3
|
||||
mcVersion=26.4-snapshot-5
|
||||
# This is the current API version for use in (paper-)plugin.yml files
|
||||
# During snapshot cycles this should be the anticipated version of the release target
|
||||
apiVersion=26.3
|
||||
channel=STABLE
|
||||
apiVersion=26.4
|
||||
channel=ALPHA
|
||||
|
||||
# Set to true while updating Minecraft version
|
||||
updatingMinecraft=false
|
||||
updatingMinecraft=true
|
||||
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.caching=true
|
||||
|
||||
@@ -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,64 +0,0 @@
|
||||
package com.destroystokyo.paper.block;
|
||||
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents the sounds that a {@link Block} makes in certain situations
|
||||
* <p>
|
||||
* The sound group includes break, step, place, hit, and fall sounds.
|
||||
* @deprecated use {@link org.bukkit.SoundGroup}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
public interface BlockSoundGroup {
|
||||
/**
|
||||
* Gets the sound that plays when breaking this block
|
||||
*
|
||||
* @return The break sound
|
||||
* @deprecated use {@link org.bukkit.SoundGroup#getBreakSound()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
Sound getBreakSound();
|
||||
|
||||
/**
|
||||
* Gets the sound that plays when stepping on this block
|
||||
*
|
||||
* @return The step sound
|
||||
* @deprecated use {@link org.bukkit.SoundGroup#getStepSound()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
Sound getStepSound();
|
||||
|
||||
/**
|
||||
* Gets the sound that plays when placing this block
|
||||
*
|
||||
* @return The place sound
|
||||
* @deprecated use {@link org.bukkit.SoundGroup#getPlaceSound()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
Sound getPlaceSound();
|
||||
|
||||
/**
|
||||
* Gets the sound that plays when hitting this block
|
||||
*
|
||||
* @return The hit sound
|
||||
* @deprecated use {@link org.bukkit.SoundGroup#getHitSound()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
Sound getHitSound();
|
||||
|
||||
/**
|
||||
* Gets the sound that plays when this block falls
|
||||
*
|
||||
* @return The fall sound
|
||||
* @deprecated use {@link org.bukkit.SoundGroup#getFallSound()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
Sound getFallSound();
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Holds the properties for this item for when it is consumed.
|
||||
@@ -30,7 +31,7 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
|
||||
ItemUseAnimation animation();
|
||||
|
||||
@Contract(pure = true)
|
||||
Key sound();
|
||||
@Nullable Key sound();
|
||||
|
||||
@Contract(pure = true)
|
||||
boolean hasConsumeParticles();
|
||||
@@ -65,11 +66,11 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
|
||||
/**
|
||||
* Sets the sound played when consuming the item.
|
||||
*
|
||||
* @param sound the {@link Key} representing the sound to be used
|
||||
* @param sound the {@link Key} representing the sound to be used, or {@code null} for no sound
|
||||
* @return the builder for chaining
|
||||
*/
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
Builder sound(Key sound);
|
||||
Builder sound(@Nullable Key sound);
|
||||
|
||||
/**
|
||||
* Sets whether consuming the item results in particle effects.
|
||||
|
||||
@@ -39,10 +39,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
|
||||
/**
|
||||
* Gets the equip sound key.
|
||||
*
|
||||
* @return the equip sound key
|
||||
* @return the equip sound key, or {@code null} if none
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
Key equipSound();
|
||||
@Nullable Key equipSound();
|
||||
|
||||
/**
|
||||
* Gets the asset id if present.
|
||||
@@ -112,10 +112,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
|
||||
/**
|
||||
* Returns the sound that is played when shearing this equipment off an entity.
|
||||
*
|
||||
* @return shear sound
|
||||
* @return shear sound, or {@code null} if none
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
Key shearSound();
|
||||
@Nullable Key shearSound();
|
||||
|
||||
/**
|
||||
* Builder for {@link Equippable}.
|
||||
@@ -126,11 +126,11 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
|
||||
/**
|
||||
* Sets the equip sound key for this item.
|
||||
*
|
||||
* @param sound the equip sound key
|
||||
* @param sound the equip sound key, or {@code null} for no sound
|
||||
* @return the builder for chaining
|
||||
*/
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
Builder equipSound(Key sound);
|
||||
Builder equipSound(@Nullable Key sound);
|
||||
|
||||
/**
|
||||
* Sets the asset id for this item.
|
||||
@@ -207,10 +207,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
|
||||
/**
|
||||
* Sets the sound that is played when shearing this equipment off an entity.
|
||||
*
|
||||
* @param shearSound the shear sound key
|
||||
* @param shearSound the shear sound key, or {@code null} for no sound
|
||||
* @return the builder for chaining
|
||||
*/
|
||||
@Contract(value = "_ -> this", mutates = "this")
|
||||
Builder shearSound(Key shearSound);
|
||||
Builder shearSound(@Nullable Key shearSound);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
+6
-6
@@ -182,16 +182,16 @@ public interface SulfurCubeArchetypeRegistryEntry {
|
||||
|
||||
// todo The sounds should take a RegistryHolder but need more thoughts on the create method
|
||||
/**
|
||||
* {@return the sound played once the sulfur cube is knocked}
|
||||
* {@return the sound played once the sulfur cube is knocked, or {@code null} if none}
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
TypedKey<Sound> hitSound();
|
||||
@Nullable TypedKey<Sound> hitSound();
|
||||
|
||||
/**
|
||||
* {@return the sound played once the sulfur cube is pushed}
|
||||
* {@return the sound played once the sulfur cube is pushed, or {@code null} if none}
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
TypedKey<Sound> pushSound();
|
||||
@Nullable TypedKey<Sound> pushSound();
|
||||
|
||||
/**
|
||||
* {@return the smallest impulse required to play the push sound}
|
||||
@@ -214,8 +214,8 @@ public interface SulfurCubeArchetypeRegistryEntry {
|
||||
*/
|
||||
@Contract(value = "_, _, _, _ -> new", pure = true)
|
||||
@ApiStatus.Experimental
|
||||
static SoundSettings of(final TypedKey<Sound> hitSound, final TypedKey<Sound> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
|
||||
record Impl(TypedKey<Sound> hitSound, TypedKey<Sound> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
|
||||
static SoundSettings of(final @Nullable TypedKey<Sound> hitSound, final @Nullable TypedKey<Sound> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
|
||||
record Impl(@Nullable TypedKey<Sound> hitSound, @Nullable TypedKey<Sound> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
|
||||
}
|
||||
|
||||
return new Impl(hitSound, pushSound, pushSoundImpulseThreshold, pushSoundCooldown);
|
||||
|
||||
@@ -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;
|
||||
@@ -874,7 +872,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 +882,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 +904,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 +924,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 +944,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.
|
||||
|
||||
@@ -1,70 +1,112 @@
|
||||
package org.bukkit;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a group of sounds for blocks that are played when various actions
|
||||
* happen (ie stepping, breaking, hitting, etc).
|
||||
* happen (ie stepping, breaking, hitting, etc.).
|
||||
*/
|
||||
@NullMarked
|
||||
public interface SoundGroup {
|
||||
|
||||
/**
|
||||
* Get the volume these sounds are played at.
|
||||
*
|
||||
* <p>
|
||||
* Note that this volume does not always represent the actual volume
|
||||
* received by the client.
|
||||
*
|
||||
* @return volume
|
||||
*/
|
||||
public float getVolume();
|
||||
float getVolume();
|
||||
|
||||
/**
|
||||
* Gets the pitch these sounds are played at.
|
||||
*
|
||||
* <p>
|
||||
* Note that this pitch does not always represent the actual pitch received
|
||||
* by the client.
|
||||
*
|
||||
* @return pitch
|
||||
*/
|
||||
public float getPitch();
|
||||
float getPitch();
|
||||
|
||||
/**
|
||||
* Gets the corresponding breaking sound for this group.
|
||||
*
|
||||
* @return the break sound
|
||||
* @return the break sound, or {@link Sound#BLOCK_STONE_BREAK} if this group has none
|
||||
* @deprecated sounds may now be nullable, use {@link #breakSound()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
public Sound getBreakSound();
|
||||
@Deprecated(since = "26.4")
|
||||
Sound getBreakSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding step sound for this group.
|
||||
*
|
||||
* @return the step sound
|
||||
* @return the step sound, or {@link Sound#BLOCK_STONE_STEP} if this group has none
|
||||
* @deprecated sounds may now be nullable, use {@link #stepSound()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
public Sound getStepSound();
|
||||
@Deprecated(since = "26.4")
|
||||
Sound getStepSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding place sound for this group.
|
||||
*
|
||||
* @return the place sound
|
||||
* @return the place sound, or {@link Sound#BLOCK_STONE_PLACE} if this group has none
|
||||
* @deprecated sounds may now be nullable, use {@link #placeSound()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
public Sound getPlaceSound();
|
||||
@Deprecated(since = "26.4")
|
||||
Sound getPlaceSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding hit sound for this group.
|
||||
*
|
||||
* @return the hit sound
|
||||
* @return the hit sound, or {@link Sound#BLOCK_STONE_HIT} if this group has none
|
||||
* @deprecated sounds may now be nullable, use {@link #hitSound()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
public Sound getHitSound();
|
||||
@Deprecated(since = "26.4")
|
||||
Sound getHitSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding fall sound for this group.
|
||||
*
|
||||
* @return the fall sound
|
||||
* @return the fall sound, or {@link Sound#BLOCK_STONE_FALL} if this group has none
|
||||
* @deprecated sounds may now be nullable, use {@link #fallSound()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
public Sound getFallSound();
|
||||
@Deprecated(since = "26.4")
|
||||
Sound getFallSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding breaking sound for this group.
|
||||
*
|
||||
* @return the break sound, or {@code null} if this group has none
|
||||
*/
|
||||
@Nullable Sound breakSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding step sound for this group.
|
||||
*
|
||||
* @return the step sound, or {@code null} if this group has none
|
||||
*/
|
||||
@Nullable Sound stepSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding place sound for this group.
|
||||
*
|
||||
* @return the place sound, or {@code null} if this group has none
|
||||
*/
|
||||
@Nullable Sound placeSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding hit sound for this group.
|
||||
*
|
||||
* @return the hit sound, or {@code null} if this group has none
|
||||
*/
|
||||
@Nullable Sound hitSound();
|
||||
|
||||
/**
|
||||
* Gets the corresponding fall sound for this group.
|
||||
*
|
||||
* @return the fall sound, or {@code null} if this group has none
|
||||
*/
|
||||
@Nullable Sound fallSound();
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -778,19 +778,6 @@ public interface Block extends Metadatable, Translatable, net.kyori.adventure.tr
|
||||
*/
|
||||
boolean canPlace(@NotNull BlockData data);
|
||||
|
||||
// Paper start
|
||||
/**
|
||||
* Gets the {@link com.destroystokyo.paper.block.BlockSoundGroup} for this block.
|
||||
* <p>
|
||||
* This object contains the block, step, place, hit, and fall sounds.
|
||||
*
|
||||
* @return the sound group for this block
|
||||
* @deprecated use {@link #getBlockSoundGroup()}
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated(forRemoval = true, since = "1.18.2")
|
||||
com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup();
|
||||
|
||||
/**
|
||||
* Gets the {@link org.bukkit.SoundGroup} for this block.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
/**
|
||||
|
||||
+2
-2
@@ -35,9 +35,9 @@ public interface EquippableComponent extends ConfigurationSerializable {
|
||||
/**
|
||||
* Gets the sound to play when the item is equipped.
|
||||
*
|
||||
* @return the sound
|
||||
* @return the sound, or null if none
|
||||
*/
|
||||
@NotNull // Paper
|
||||
@Nullable
|
||||
Sound getEquipSound();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.4-snapshot-5+build.2")
|
||||
paperclip("io.papermc:paperclip:3.0.4")
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ paperweight {
|
||||
gitFilePatches = false
|
||||
|
||||
updatingMinecraft {
|
||||
//oldPaperCommit = "e5fe71723e2ffde7cc9fafc085ac3bb73e63175e"
|
||||
oldPaperCommit = "8c0b8396d676f6abef3df8ab992ac726f6e01c9c"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
@@ -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 }
|
||||
|
||||
File renamed without changes.
+5
-5
@@ -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) {
|
||||
+105
-1108
File diff suppressed because it is too large.
Load diff
+6
-6
@@ -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<?>> {
|
||||
File renamed without changes.
File renamed without changes.
+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,
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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
|
||||
+6
-6
@@ -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
|
||||
File renamed without changes.
+4
-4
@@ -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
|
||||
File renamed without changes.
@@ -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.
@@ -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;
|
||||
+ }
|
||||
|
||||
+9
-9
@@ -1,25 +1,25 @@
|
||||
--- a/com/mojang/authlib/services/MinecraftServicesDiscoveryService.java
|
||||
+++ b/com/mojang/authlib/services/MinecraftServicesDiscoveryService.java
|
||||
@@ -37,7 +_,7 @@
|
||||
private final boolean servicesKeySetEnabled;
|
||||
@@ -40,7 +_,7 @@
|
||||
private final Supplier<DiscoveryResponse> discoverySupplier;
|
||||
private final String clientVersion;
|
||||
|
||||
- private MinecraftServicesDiscoveryService(
|
||||
+ protected MinecraftServicesDiscoveryService( // Paper - private->protected
|
||||
final Proxy proxy,
|
||||
final boolean servicesKeySetEnabled,
|
||||
final Supplier<DiscoveryResponse> discoverySupplier
|
||||
@@ -68,7 +_,7 @@
|
||||
final Supplier<DiscoveryResponse> discoverySupplier,
|
||||
@@ -75,7 +_,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
- private static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment) {
|
||||
+ protected static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment) { // Paper - private->protected
|
||||
final MinecraftClient client = MinecraftClient.unauthenticated(proxy);
|
||||
- private static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment, final String clientVersion) {
|
||||
+ protected static Supplier<DiscoveryResponse> createDiscoverySupplier(final Proxy proxy, final Environment environment, final String clientVersion) { // Paper - private->protected
|
||||
final MinecraftClient client = MinecraftClient.unauthenticated(proxy, clientVersion);
|
||||
return RetryableFetch.fetch(
|
||||
() -> HttpDiscoveryService.constantURL(environment.discoveryUrl()),
|
||||
@@ -84,7 +_,7 @@
|
||||
return new MinecraftServicesDiscoveryService(proxy, false, DiscoveryResponse::offline);
|
||||
@@ -95,7 +_,7 @@
|
||||
return clientVersion;
|
||||
}
|
||||
|
||||
- private static Environment determineEnvironment() {
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
--- a/com/mojang/authlib/services/MinecraftServicesProfileRepository.java
|
||||
+++ b/com/mojang/authlib/services/MinecraftServicesProfileRepository.java
|
||||
@@ -42,6 +_,8 @@
|
||||
@@ -44,6 +_,8 @@
|
||||
.filter(name -> !Strings.isNullOrEmpty(name))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
|
||||
final List<String> normalizedRequest = request.stream().map(MinecraftServicesProfileRepository::normalizeName).toList();
|
||||
|
||||
@@ -73,6 +_,13 @@
|
||||
@@ -75,6 +_,13 @@
|
||||
callback.onProfileLookupFailed(name, new ProfileNotFoundException("Server did not find the requested profile"));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
try {
|
||||
Thread.sleep(DELAY_BETWEEN_PAGES);
|
||||
} catch (final InterruptedException ignored) {
|
||||
@@ -102,7 +_,15 @@
|
||||
@@ -104,7 +_,15 @@
|
||||
try {
|
||||
return Optional.ofNullable(client.get(HttpDiscoveryService.constantURL(discoveryService.getUrl(Service.PROFILES, "getByName").replace("{name}", normalizeName(name))), NameAndId.class));
|
||||
} catch (final MinecraftClientException e) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
this.nodes.remove(node.holder().id());
|
||||
}
|
||||
|
||||
@@ -46,7 +_,7 @@
|
||||
@@ -47,7 +_,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +113,8 @@
|
||||
|
||||
@Override
|
||||
public Collection<String> getOnlinePlayerNames() {
|
||||
- return Lists.newArrayList(this.server.getPlayerNames());
|
||||
+ return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().name()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
|
||||
- return List.of(this.server.getPlayerNames());
|
||||
+ return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().name()).toList() : List.of(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -159,6 +_,7 @@
|
||||
@@ -160,6 +_,7 @@
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class Commands {
|
||||
@@ -8,7 +8,7 @@
|
||||
public static final String COMMAND_PREFIX = "/";
|
||||
private static final ThreadLocal<@Nullable ExecutionContext<CommandSourceStack>> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>();
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
@@ -183,6 +_,7 @@
|
||||
@@ -184,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public boolean isRestricted(final CommandNode<CommandSourceStack> node) {
|
||||
@@ -16,7 +16,7 @@
|
||||
Predicate<CommandSourceStack> requirement = node.getRequirement();
|
||||
return !requirement.test(this.noPermissionSource);
|
||||
}
|
||||
@@ -190,6 +_,11 @@
|
||||
@@ -191,6 +_,11 @@
|
||||
private final CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher<>();
|
||||
|
||||
public Commands(final Commands.CommandSelection commandSelection, final CommandBuildContext context) {
|
||||
@@ -28,7 +28,7 @@
|
||||
AdvancementCommands.register(this.dispatcher);
|
||||
AttributeCommand.register(this.dispatcher, context);
|
||||
ExecuteCommand.register(this.dispatcher, context);
|
||||
@@ -304,6 +_,42 @@
|
||||
@@ -305,6 +_,42 @@
|
||||
UnpublishCommand.register(this.dispatcher);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
|
||||
}
|
||||
|
||||
@@ -323,6 +_,13 @@
|
||||
@@ -324,6 +_,13 @@
|
||||
}
|
||||
|
||||
public void performCommand(final ParseResults<CommandSourceStack> command, final String commandString) {
|
||||
@@ -85,7 +85,7 @@
|
||||
CommandSourceStack sender = command.getContext().getSource();
|
||||
Profiler.get().push(() -> "/" + commandString);
|
||||
ContextChain<CommandSourceStack> commandChain = finishParsing(command, commandString, sender);
|
||||
@@ -336,10 +_,12 @@
|
||||
@@ -337,10 +_,12 @@
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -101,7 +101,7 @@
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
|
||||
for (int i = 0; i < Math.min(stackTrace.length, 3); i++) {
|
||||
@@ -370,12 +_,16 @@
|
||||
@@ -371,12 +_,16 @@
|
||||
return ContextChain.tryFlatten(command.getContext().build(commandString))
|
||||
.orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(command.getReader()));
|
||||
} catch (CommandSyntaxException e) {
|
||||
@@ -120,7 +120,7 @@
|
||||
if (cursor > 10) {
|
||||
context.append(CommonComponents.ELLIPSIS);
|
||||
}
|
||||
@@ -387,7 +_,17 @@
|
||||
@@ -388,7 +_,17 @@
|
||||
}
|
||||
|
||||
context.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
|
||||
@@ -139,7 +139,7 @@
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -415,10 +_,65 @@
|
||||
@@ -416,10 +_,65 @@
|
||||
}
|
||||
|
||||
public void sendCommands(final ServerPlayer player) {
|
||||
@@ -207,7 +207,7 @@
|
||||
player.connection.send(new ClientboundCommandsPacket(root, COMMAND_NODE_INSPECTOR));
|
||||
}
|
||||
|
||||
@@ -426,8 +_,49 @@
|
||||
@@ -427,8 +_,49 @@
|
||||
final CommandNode<S> source, final CommandNode<S> target, final S commandFilter, final Map<CommandNode<S>, CommandNode<S>> converted
|
||||
) {
|
||||
for (CommandNode<S> child : source.getChildren()) {
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@
|
||||
private final StringReader reader;
|
||||
private final boolean forTesting;
|
||||
private final boolean allowNbt;
|
||||
- private final Map<Property<?>, Comparable<?>> properties = Maps.newHashMap();
|
||||
+ private final Map<Property<?>, Comparable<?>> properties = Maps.newLinkedHashMap(); // CraftBukkit - stable
|
||||
private final Map<String, String> vagueProperties = Maps.newHashMap();
|
||||
- private final Map<Property<?>, Comparable<?>> properties = new HashMap<>();
|
||||
+ private final Map<Property<?>, Comparable<?>> properties = new java.util.LinkedHashMap<>(); // CraftBukkit - stable
|
||||
private final Map<String, String> vagueProperties = new HashMap<>();
|
||||
private Identifier id = Identifier.withDefaultNamespace("");
|
||||
private @Nullable StateDefinition<Block, BlockState> definition;
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/BlockPos.java
|
||||
+++ b/net/minecraft/core/BlockPos.java
|
||||
@@ -141,67 +_,84 @@
|
||||
@@ -139,67 +_,84 @@
|
||||
|
||||
@Override
|
||||
public BlockPos above() {
|
||||
@@ -98,7 +98,7 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -667,9 +_,9 @@
|
||||
@@ -698,9 +_,9 @@
|
||||
}
|
||||
|
||||
public BlockPos.MutableBlockPos set(final int x, final int y, final int z) {
|
||||
@@ -111,7 +111,7 @@
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -727,19 +_,19 @@
|
||||
@@ -758,19 +_,19 @@
|
||||
|
||||
@Override
|
||||
public BlockPos.MutableBlockPos setX(final int x) {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- a/net/minecraft/core/Holder.java
|
||||
+++ b/net/minecraft/core/Holder.java
|
||||
@@ -252,7 +_,7 @@
|
||||
}
|
||||
|
||||
void bindTags(final Collection<TagKey<T>> tags) {
|
||||
- this.tags = Set.copyOf(tags);
|
||||
+ this.tags = it.unimi.dsi.fastutil.objects.ReferenceSets.unmodifiable(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(tags)); // Paper - use reference set because TagKey are interned
|
||||
}
|
||||
|
||||
public void bindComponents(final DataComponentMap components) {
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/MappedRegistry.java
|
||||
+++ b/net/minecraft/core/MappedRegistry.java
|
||||
@@ -33,17 +_,25 @@
|
||||
@@ -34,17 +_,25 @@
|
||||
public class MappedRegistry<T> implements WritableRegistry<T> {
|
||||
private final ResourceKey<? extends Registry<T>> key;
|
||||
private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256);
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
@Override
|
||||
public Stream<HolderSet.Named<T>> listTags() {
|
||||
@@ -117,6 +_,7 @@
|
||||
@@ -118,6 +_,7 @@
|
||||
this.toId.put(value, newId);
|
||||
this.registrationInfos.put(key, registrationInfo);
|
||||
this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle());
|
||||
@@ -39,7 +39,7 @@
|
||||
return holder;
|
||||
}
|
||||
|
||||
@@ -278,6 +_,7 @@
|
||||
@@ -279,6 +_,7 @@
|
||||
}
|
||||
|
||||
this.frozen = true;
|
||||
@@ -47,7 +47,7 @@
|
||||
this.byValue.forEach((value, holder) -> holder.bindValue((T)value));
|
||||
List<Identifier> unboundEntries = this.byKey
|
||||
.entrySet()
|
||||
@@ -517,4 +_,13 @@
|
||||
@@ -518,4 +_,13 @@
|
||||
|
||||
Stream<HolderSet.Named<T>> getTags();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/Vec3i.java
|
||||
+++ b/net/minecraft/core/Vec3i.java
|
||||
@@ -22,9 +_,9 @@
|
||||
@@ -20,9 +_,9 @@
|
||||
ByteBufCodecs.VAR_INT, Vec3i::getX, ByteBufCodecs.VAR_INT, Vec3i::getY, ByteBufCodecs.VAR_INT, Vec3i::getZ, Vec3i::new
|
||||
);
|
||||
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
public static Codec<Vec3i> offsetCodec(final int maxOffsetPerAxis) {
|
||||
return CODEC.validate(
|
||||
@@ -41,12 +_,12 @@
|
||||
@@ -39,12 +_,12 @@
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,7 +28,7 @@
|
||||
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
}
|
||||
|
||||
@@ -59,15 +_,15 @@
|
||||
@@ -57,15 +_,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
return this.z;
|
||||
}
|
||||
|
||||
@@ -251,4 +_,11 @@
|
||||
@@ -249,4 +_,11 @@
|
||||
public String toShortString() {
|
||||
return this.getX() + ", " + this.getY() + ", " + this.getZ();
|
||||
}
|
||||
|
||||
+2
-2
@@ -236,8 +236,8 @@
|
||||
hand,
|
||||
itemInHand,
|
||||
Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3),
|
||||
- SoundEvents.BUCKET_EMPTY_POWDER_SNOW
|
||||
+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection // Paper - add hitDirection
|
||||
- SoundEvents.BUCKET_EMPTY_POWDER_SNOW.value()
|
||||
+ SoundEvents.BUCKET_EMPTY_POWDER_SNOW.value(), hitDirection // Paper - add hitDirection
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/component/BlockTransformer.java
|
||||
+++ b/net/minecraft/core/component/BlockTransformer.java
|
||||
@@ -68,6 +_,14 @@
|
||||
@@ -64,6 +_,14 @@
|
||||
BlockState updatedShape = transformData.updateFromNeighbors ? Block.updateFromNeighbourShapes(newBlockState, level, pos) : newBlockState;
|
||||
Player player = context.getPlayer();
|
||||
ItemStack itemInHand = context.getItemInHand();
|
||||
|
||||
+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);
|
||||
|
||||
+48
-56
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
@@ -80,12 +_,38 @@
|
||||
@@ -83,13 +_,39 @@
|
||||
Direction direction = source.state().getValue(DispenserBlock.FACING);
|
||||
BlockPos pos = source.pos().relative(direction);
|
||||
ServerLevel serverLevel = source.level();
|
||||
@@ -36,17 +36,17 @@
|
||||
);
|
||||
ArmorStand armorStand = EntityTypes.ARMOR_STAND.spawn(serverLevel, postSpawnConfig, pos, EntitySpawnReason.DISPENSER, false, false);
|
||||
if (armorStand != null) {
|
||||
ArmorStandItem.playSound(serverLevel, armorStand);
|
||||
- dispensed.shrink(1);
|
||||
+ if (shrink) dispensed.shrink(1); // Paper
|
||||
}
|
||||
|
||||
return dispensed;
|
||||
@@ -103,8 +_,33 @@
|
||||
@@ -107,9 +_,34 @@
|
||||
.getEntitiesOfClass(AbstractChestedHorse.class, new AABB(pos), entity -> entity.isAlive() && !entity.hasChest())) {
|
||||
if (abstractChestedHorse.isTamed()) {
|
||||
SlotAccess slot = abstractChestedHorse.getSlot(AbstractHorse.CHEST_SLOT_OFFSET);
|
||||
- if (slot != null && slot.set(dispensed)) {
|
||||
- dispensed.shrink(1);
|
||||
+ // CraftBukkit start
|
||||
+ if (slot != null/* && slot.set(dispensed)*/) {
|
||||
+ ItemStack singleCopy = dispensed.copyWithCount(1);
|
||||
@@ -72,12 +72,14 @@
|
||||
+ }
|
||||
+ slot.set(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
|
||||
+ // CraftBukkit end
|
||||
abstractChestedHorse.playChestEquipsSound();
|
||||
- dispensed.shrink(1);
|
||||
+
|
||||
+ if (shrink) dispensed.shrink(1); // Paper - actually handle here
|
||||
this.setSuccess(true);
|
||||
return dispensed;
|
||||
}
|
||||
@@ -145,8 +_,45 @@
|
||||
@@ -150,8 +_,45 @@
|
||||
DispensibleContainerItem bucket = (DispensibleContainerItem)dispensed.getItem();
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
Level level = source.level();
|
||||
@@ -88,7 +90,7 @@
|
||||
+ /* Taken from SolidBucketItem#emptyContents */
|
||||
+ boolean willEmptyContentsSolidBucketItem = bucket instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(target) && state.isAir();
|
||||
+ /* Taken from BucketItem#emptyContents */
|
||||
+ boolean willEmptyBucketItem = bucket instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.getContent() instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.getContent()) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, target, state, bucketItem.getContent())));
|
||||
+ boolean willEmptyBucketItem = bucket instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.getContent() instanceof net.minecraft.world.level.material.FlowingFluid && (state.isAir() || state.canBeReplaced(bucketItem.getContent()) || (state.getBlock() instanceof net.minecraft.world.level.block.LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, target, state, bucketItem.getContent().defaultFluidState())));
|
||||
+ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
|
||||
+ // Paper end - correctly check if the bucket place will succeed
|
||||
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, source.pos());
|
||||
@@ -124,7 +126,7 @@
|
||||
return this.consumeWithRemainder(source, dispensed, new ItemStack(Items.BUCKET));
|
||||
} else {
|
||||
return this.defaultDispenseItemBehavior.dispense(source, dispensed);
|
||||
@@ -170,13 +_,20 @@
|
||||
@@ -175,7 +_,7 @@
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
BlockState blockState = level.getBlockState(target);
|
||||
if (blockState.getBlock() instanceof BucketPickup bucket) {
|
||||
@@ -133,20 +135,21 @@
|
||||
if (pickup.isEmpty()) {
|
||||
return super.execute(source, dispensed);
|
||||
}
|
||||
|
||||
@@ -183,6 +_,13 @@
|
||||
bucket.getPickupSound().ifPresent(soundEvent -> level.playSound(null, target, soundEvent, SoundSource.BLOCKS));
|
||||
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 {
|
||||
return super.execute(source, dispensed);
|
||||
@@ -190,11 +_,54 @@
|
||||
@@ -196,11 +_,54 @@
|
||||
this.setSuccess(true);
|
||||
Level level = source.level();
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
@@ -201,7 +204,7 @@
|
||||
|
||||
return dispensed;
|
||||
}
|
||||
@@ -209,15 +_,41 @@
|
||||
@@ -215,15 +_,41 @@
|
||||
}
|
||||
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
@@ -250,21 +253,21 @@
|
||||
this.setSuccess(true);
|
||||
return dispensed;
|
||||
}
|
||||
@@ -231,6 +_,13 @@
|
||||
@@ -236,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 @@
|
||||
@@ -248,7 +_,7 @@
|
||||
dispensed.shrink(1);
|
||||
this.setSuccess(true);
|
||||
} else {
|
||||
@@ -273,21 +276,21 @@
|
||||
}
|
||||
|
||||
return dispensed;
|
||||
@@ -256,6 +_,13 @@
|
||||
@@ -261,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 @@
|
||||
@@ -270,7 +_,7 @@
|
||||
dispensed.shrink(1);
|
||||
this.setSuccess(true);
|
||||
} else {
|
||||
@@ -296,31 +299,20 @@
|
||||
}
|
||||
|
||||
return dispensed;
|
||||
@@ -289,11 +_,23 @@
|
||||
@@ -296,6 +_,12 @@
|
||||
ServerLevel level = source.level();
|
||||
BlockPos target = source.pos().relative(source.state().getValue(DispenserBlock.FACING));
|
||||
BlockState state = level.getBlockState(target);
|
||||
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
|
||||
+ // 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.getBlock() instanceof BeehiveBlock) && state.getValueOrElse(BeehiveBlock.HONEY_LEVEL, 0) >= 5) {
|
||||
((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 @@
|
||||
@@ -320,6 +_,13 @@
|
||||
this.setSuccess(true);
|
||||
if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
|
||||
if (blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) {
|
||||
@@ -334,7 +326,7 @@
|
||||
RespawnAnchorBlock.charge(null, level, pos, blockState);
|
||||
dispensed.shrink(1);
|
||||
} else {
|
||||
@@ -336,6 +_,28 @@
|
||||
@@ -344,6 +_,28 @@
|
||||
return dispensed;
|
||||
}
|
||||
|
||||
@@ -363,7 +355,7 @@
|
||||
for (Armadillo armadillo : armadillos) {
|
||||
if (armadillo.brushOffScute(null, dispensed)) {
|
||||
dispensed.hurtAndBreak(16, level, null, item -> {});
|
||||
@@ -355,6 +_,13 @@
|
||||
@@ -363,6 +_,13 @@
|
||||
BlockState blockState = level.getBlockState(pos);
|
||||
Optional<BlockState> maybeWaxed = HoneycombItem.getWaxed(blockState);
|
||||
if (maybeWaxed.isPresent()) {
|
||||
@@ -377,7 +369,7 @@
|
||||
level.setBlockAndUpdate(pos, maybeWaxed.get());
|
||||
level.levelEvent(LevelEvent.PARTICLES_WAX_ON, pos, 0);
|
||||
level.playSound(null, pos, SoundEvents.HONEYCOMB_WAX_ON, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||
@@ -385,6 +_,12 @@
|
||||
@@ -393,6 +_,12 @@
|
||||
return this.defaultDispenseItemBehavior.dispense(source, dispensed);
|
||||
}
|
||||
|
||||
|
||||
+35
-70
@@ -1,80 +1,45 @@
|
||||
--- a/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/FlintAndSteelDispenseItemBehavior.java
|
||||
@@ -25,21 +_,55 @@
|
||||
@@ -24,8 +_,15 @@
|
||||
ServerLevel level = source.level();
|
||||
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)
|
||||
+ ) {
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
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));
|
||||
level.gameEvent(null, GameEvent.BLOCK_PLACE, targetPos);
|
||||
+ }
|
||||
+ // 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
|
||||
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) {
|
||||
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(source, targetPos, dispensed, this);
|
||||
+ if (result != null) {
|
||||
+ this.setSuccess(false);
|
||||
+ return blockDispenseEventResult[0];
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Paper end - Call BlockDispenseEvent - yield back possibly changed result stack from sulfur or tnt dispense events
|
||||
|
||||
+ // Paper end - Call BlockDispenseEvent
|
||||
BlockState target = level.getBlockState(targetPos);
|
||||
- boolean ignited = tryIgnite(dispensed, level, targetPos, facing, target);
|
||||
+ boolean ignited = tryIgnite(dispensed, level, targetPos, facing, target, source.pos()); // CraftBukkit
|
||||
this.setSuccess(ignited);
|
||||
if (this.isSuccess()) {
|
||||
dispensed.hurtAndBreak(1, level, null, item -> {});
|
||||
@@ -48,11 +_,18 @@
|
||||
return dispensed;
|
||||
FlintAndSteelItem.playSound(level, targetPos);
|
||||
@@ -36,19 +_,23 @@
|
||||
}
|
||||
|
||||
- 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;
|
||||
private static boolean tryIgnite(
|
||||
- final ItemStack dispensed, final ServerLevel level, final BlockPos targetPos, final Direction facing, final BlockState target
|
||||
+ final ItemStack dispensed, final ServerLevel level, final BlockPos targetPos, final Direction facing, final BlockState target, final BlockPos sourcePos // CraftBukkit
|
||||
) {
|
||||
if (tryIgniteExplosiveEntities(level, targetPos)) {
|
||||
return true;
|
||||
} else if (BaseFireBlock.canBePlacedAt(level, targetPos, facing)) {
|
||||
+ // CraftBukkit start - Ignition by dispensing flint and steel
|
||||
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(level, targetPos, sourcePos).isCancelled()) {
|
||||
level.setBlockAndUpdate(targetPos, BaseFireBlock.getState(level, targetPos));
|
||||
level.gameEvent(null, GameEvent.BLOCK_PLACE, targetPos);
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
return true;
|
||||
} else if (CampfireBlock.canLight(target) || CandleBlock.canLight(target) || CandleCakeBlock.canLight(target)) {
|
||||
level.setBlockAndUpdate(targetPos, target.setValue(BlockStateProperties.LIT, true));
|
||||
level.gameEvent(null, GameEvent.BLOCK_CHANGE, targetPos);
|
||||
return true;
|
||||
- } else if (target.getBlock() instanceof TntBlock && TntBlock.prime(level, targetPos, null, dispensed)) {
|
||||
+ } else if (target.getBlock() instanceof TntBlock && TntBlock.prime(level, targetPos, null, dispensed, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, targetPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, sourcePos))) { // CraftBukkit - TNTPrimeEvent
|
||||
level.removeBlock(targetPos, false);
|
||||
return true;
|
||||
} else {
|
||||
+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;
|
||||
}
|
||||
+7
-49
@@ -1,65 +1,23 @@
|
||||
--- a/net/minecraft/core/registries/BuiltInRegistries.java
|
||||
+++ b/net/minecraft/core/registries/BuiltInRegistries.java
|
||||
@@ -367,6 +_,11 @@
|
||||
public static final Registry<ContextKeySet> CONTEXT_KEY_SET = registerSimple(Registries.CONTEXT_KEY_SET, LootContextParamSets::bootstrap);
|
||||
public static final Registry<Consumer<GameTestHelper>> TEST_FUNCTION = registerSimple(Registries.TEST_FUNCTION, BuiltinTestFunctions::bootstrap);
|
||||
public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
|
||||
@@ -373,9 +_,19 @@
|
||||
public static final Registry<ContextKeySet> CONTEXT_KEY_SET = BUILDER.registerSimple(Registries.CONTEXT_KEY_SET, LootContextParamSets::bootstrap);
|
||||
public static final Registry<Consumer<GameTestHelper>> TEST_FUNCTION = BUILDER.registerSimple(Registries.TEST_FUNCTION, BuiltinTestFunctions::bootstrap);
|
||||
public static final Registry<? extends Registry<?>> REGISTRY = BUILDER.build();
|
||||
+ // Paper start - add built-in registry conversions
|
||||
+ public static final io.papermc.paper.registry.data.util.Conversions STATIC_ACCESS_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(
|
||||
+ new net.minecraft.resources.RegistryOps.HolderLookupAdapter(net.minecraft.server.RegistryLayer.STATIC_ACCESS)
|
||||
+ );
|
||||
+ // Paper end - add built-in registry conversions
|
||||
|
||||
private static <T> Registry<T> registerSimple(final ResourceKey<? extends Registry<T>> name, final BuiltInRegistries.RegistryBootstrap<T> loader) {
|
||||
return internalRegister(name, new MappedRegistry<>(name, Lifecycle.stable(), false), loader);
|
||||
@@ -394,6 +_,7 @@
|
||||
final ResourceKey<? extends Registry<T>> name, final R registry, final BuiltInRegistries.RegistryBootstrap<T> loader
|
||||
) {
|
||||
Bootstrap.checkBootstrapCalled(() -> "registry " + name.identifier());
|
||||
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry); // Paper - initialize API registry
|
||||
Identifier key = name.identifier();
|
||||
LOADERS.put(key, () -> loader.run(registry));
|
||||
WRITABLE_REGISTRY.register((ResourceKey)name, registry, RegistrationInfo.BUILT_IN);
|
||||
@@ -401,16 +_,34 @@
|
||||
}
|
||||
|
||||
public static void bootStrap() {
|
||||
- BUILDER.bootStrap();
|
||||
+ // Paper start
|
||||
+ bootStrap(() -> {});
|
||||
+ }
|
||||
+ public static void bootStrap(Runnable runnable) {
|
||||
+ BUILDER.bootStrap(runnable);
|
||||
+ // Paper end
|
||||
+ REGISTRY.freeze(); // Paper - freeze main registry early
|
||||
createContents();
|
||||
+ runnable.run(); // Paper
|
||||
freeze();
|
||||
validate(REGISTRY);
|
||||
}
|
||||
|
||||
private static void createContents() {
|
||||
+ // Paper start - class-load org.bukkit.Registry
|
||||
+ // we have to class-load Registry here to create all the CraftRegistry instances
|
||||
+ // that will be created when Registry is class-loaded before RegistryAccess#getRegistry
|
||||
+ // would try to create them in lockReferenceHolder
|
||||
+ try {
|
||||
+ Class.forName(org.bukkit.Registry.class.getName());
|
||||
+ } catch (final ClassNotFoundException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ // Paper end - class-load org.bukkit.Registry
|
||||
LOADERS.forEach((key, value) -> {
|
||||
if (value.get() == null) {
|
||||
LOGGER.error("Unable to bootstrap registry '{}'", key);
|
||||
}
|
||||
+ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(key)); // Paper - lock reference holder creation
|
||||
});
|
||||
}
|
||||
|
||||
@@ -419,6 +_,7 @@
|
||||
|
||||
for (Registry<?> registry : REGISTRY) {
|
||||
bindBootstrappedTagsToEmpty(registry);
|
||||
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), STATIC_ACCESS_CONVERSIONS); // Paper
|
||||
registry.freeze();
|
||||
}
|
||||
}
|
||||
public static <T> HolderGetter<T> acquireBootstrapRegistrationLookup(final Registry<T> registry) {
|
||||
Loaded 100 of 400 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user